On nous a proposé d'utiliser l'analyseur PVS-Studio pour vérifier la collection de bibliothèques PMDK ouvertes destinées au développement et au débogage d'applications avec prise en charge de la mémoire non volatile. En fait, pourquoi pas. De plus, il s'agit d'un petit projet en C et C ++ avec une taille de base de code totale d'environ 170 KLOC, à l'exclusion des commentaires. Cela signifie que l'examen des résultats de l'analyse ne prendra pas beaucoup de temps et d'efforts. Allons-y.
Pour analyser le code source, l'outil PVS-Studio version 7.08 sera utilisé. Les lecteurs de notre blog, naturellement, connaissent depuis longtemps notre outil, et je ne m'étendrai pas dessus. Pour ceux qui sont venus nous voir pour la première fois, je suggère de se référer à l'article " Comment visualiser rapidement les avertissements intéressants que l'analyseur PVS-Studio donne pour le code C et C ++? " Et d' essayer une version d'essai gratuite de l'analyseur.
Cette fois, je vais regarder à l'intérieur du projet PMDK et vous parler des erreurs et des lacunes que j'ai remarquées. Dans mon ressenti intérieur, il n'y en avait pas beaucoup, ce qui indique la bonne qualité du code de ce projet. Parmi les choses intéressantes, nous pouvons noter que plusieurs fragments du mauvais code ont été trouvés, ce qui fonctionne néanmoins correctement :). Ce que je veux dire deviendra plus clair à la suite de la narration.
En résumé, le PMDK est une collection de bibliothèques et d'outils open source conçus pour simplifier le développement, le débogage et la gestion des applications à mémoire non volatile. Plus de détails ici: PMDK Introduction . Sources ici: pmdk .
Voyons quelles erreurs et lacunes je peux y trouver. Je dois dire tout de suite que j'étais loin d'être toujours attentif lors de l'analyse du rapport et que j'aurais pu manquer beaucoup de choses. Par conséquent, j'exhorte les auteurs du projet à ne pas se laisser guider exclusivement par cet article lors de la correction des défauts, mais à vérifier vous-même le code. Et pour écrire un article, ce que j'ai écrit, en regardant la liste des avertissements, me suffira :).
Mauvais code qui fonctionne
Taille d'allocation de mémoire
Il n'est pas rare que les programmeurs passent du temps à déboguer le code lorsque le programme ne se comporte pas comme il le devrait. Cependant, il existe parfois des situations où le programme fonctionne correctement, mais le code contient une erreur. Le programmeur a juste de la chance et l'erreur ne se manifeste pas. Dans le projet PMDK, j'ai rencontré plusieurs situations intéressantes à la fois et j'ai donc décidé de les rassembler dans un chapitre séparé.
int main(int argc, char *argv[])
{
....
struct pool *pop = malloc(sizeof(pop));
....
}
Avertissement PVS-Studio: V568 Il est étrange que l'opérateur 'sizeof ()' évalue la taille d'un pointeur vers une classe, mais pas la taille de l'objet de classe 'pop'. util_ctl.c 717
Une faute de frappe classique qui provoque l' allocation d'une mauvaise quantité de mémoire. L'opérateur sizeof renverra non pas la taille de la structure, mais la taille du pointeur vers cette structure. La bonne option serait:
struct pool *pop = malloc(sizeof(pool));
ou
struct pool *pop = malloc(sizeof(*pop));
Cependant, ce code mal écrit fonctionne très bien. Le fait est que la structure du pool contient exactement un pointeur:
struct pool {
struct ctl *ctl;
};
Il s'avère que la structure occupe exactement autant que le pointeur. Les choses sont bonnes.
Longueur de la ligne
Passons au cas suivant, où l'erreur a de nouveau été commise à l'aide de l'opérateur sizeof .
typedef void *(*pmem2_memcpy_fn)(void *pmemdest, const void *src, size_t len,
unsigned flags);
static const char *initial_state = "No code.";
static int
test_rwx_prot_map_priv_do_execute(const struct test_case *tc,
int argc, char *argv[])
{
....
char *addr_map = pmem2_map_get_address(map);
map->memcpy_fn(addr_map, initial_state, sizeof(initial_state), 0);
....
}
Avertissement PVS-Studio: V579 [CWE-687] La fonction memcpy_fn reçoit le pointeur et sa taille comme arguments. C'est peut-être une erreur. Inspectez le troisième argument. pmem2_map_prot.c 513
Un pointeur vers une fonction de copie spéciale est utilisé pour copier une chaîne. Faites attention à l'appel de cette fonction, ou plutôt à son troisième argument.
Le programmeur suppose que l'opérateur sizeof calculera la taille de la chaîne littérale. Mais, en fait, la taille du pointeur est à nouveau calculée.
Heureusement, la chaîne se compose de 8 caractères et sa taille est la même que celle du pointeur lors de la création d'une application 64 bits. Par conséquent, les 8 caractères de la chaîne «Aucun code». sera copié avec succès.
En fait, la situation est encore plus compliquée et intéressante. L'interprétation de cette erreur dépend du fait que vous vouliez copier le terminal zéro ou non. Considérons deux scénarios.
Scénario 1. Il était nécessaire de copier le terminal zéro. Alors je me trompe, et ce n'est pas du tout une erreur anodine qui ne se manifeste pas. Pas 9 octets ont été copiés, mais seulement 8. Il n'y a pas de terminal zéro et les conséquences ne peuvent être prédites. Dans ce cas, le code peut être corrigé en modifiant la définition de la chaîne constante initial_state comme suit:
static const char initial_state [] = "No code.";
La valeur sizeof (initial_state) est maintenant 9.
Scénario 2. Le terminal zéro n'est pas du tout requis. Par exemple, ci-dessous, vous pouvez voir la ligne de code suivante:
UT_ASSERTeq(memcmp(addr_map, initial_state, strlen(initial_state)), 0);
Comme vous pouvez le voir, la fonction strlen renverra la valeur 8 et le terminal zéro n'est pas impliqué dans la comparaison. Alors c'est vraiment de la chance et tout va bien.
Décalage de bits
L'exemple suivant est lié à une opération de décalage au niveau du bit.
static int
clo_parse_single_uint(struct benchmark_clo *clo, const char *arg, void *ptr)
{
....
uint64_t tmax = ~0 >> (64 - 8 * clo->type_uint.size);
....
}
Avertissement PVS-Studio: V610 [CWE-758] Comportement non spécifié. Vérifiez l'opérateur de décalage «>>». L'opérande de gauche '~ 0' est négatif. clo.cpp 205
Le résultat du déplacement d'une valeur négative vers la droite dépend de l'implémentation du compilateur. Par conséquent, bien qu'un tel code puisse fonctionner correctement et comme prévu dans tous les modes de compilation d'application existants, c'est toujours de la chance.
Priorité des opérations
Et considérons le dernier cas lié à la priorité des opérations.
#define BTT_CREATE_DEF_SIZE (20 * 1UL << 20) /* 20 MB */
Avertissement PVS-Studio: V634 [CWE-783] La priorité de l'opération '*' est supérieure à celle de l'opération '<<'. Il est possible que des parenthèses soient utilisées dans l'expression. bttcreate.c 204
Pour obtenir une constante égale à 20 Mo, le programmeur a décidé de faire ce qui suit:
- Décalé de 1 de 20 bits pour obtenir 1048576, c'est-à-dire 1 Mo.
- Multiplié 1 Mo par 20.
En d'autres termes, le programmeur pense que les calculs se font comme ceci: (20 * (1UL << 20))
Mais en fait, la priorité de l'opérateur de multiplication est supérieure à celle de l'opérateur de décalage, et l'expression est évaluée comme ceci: ((20 * 1UL) << 20).
D'accord, le programmeur voulait à peine que l'expression soit évaluée dans une telle séquence. Il ne sert à rien de multiplier 20 par 1. C'est donc le cas lorsque le code ne fonctionne pas comme le programmeur l'avait prévu.
Mais cette erreur ne se manifestera en aucune façon. Peu importe comment vous écrivez:
- (20 * 1UL << 20)
- (20 * (1UL << 20))
- ((20 * 1UL) << 20)
Le résultat est toujours le même ! La valeur souhaitée est toujours 20971520 et le programme fonctionne parfaitement correctement.
Autres erreurs
Parenthèses au mauvais endroit
#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004
static void
enum_handles(int op)
{
....
NTSTATUS status;
while ((status = NtQuerySystemInformation(
SystemExtendedHandleInformation,
hndl_info, hi_size, &req_size)
== STATUS_INFO_LENGTH_MISMATCH)) {
hi_size = req_size + 4096;
hndl_info = (PSYSTEM_HANDLE_INFORMATION_EX)REALLOC(hndl_info,
hi_size);
}
UT_ASSERT(status >= 0);
....
}
Avertissement PVS-Studio: V593 [CWE-783] Pensez à revoir l'expression du genre 'A = B == C'. L'expression est calculée comme suit: «A = (B == C)». ut.c 641
Regardez de près ici:
while ((status = NtQuerySystemInformation(....) == STATUS_INFO_LENGTH_MISMATCH))
Le programmeur voulait stocker dans la variable d' état la valeur renvoyée par la fonction NtQuerySystemInformation , puis la comparer avec une constante.
Le programmeur savait très probablement que l'opérateur de comparaison (==) a une priorité plus élevée que l'opérateur d'affectation (=), et par conséquent, des parenthèses doivent être utilisées. Mais il s'est scellé et les a mis au mauvais endroit. En conséquence, les parenthèses n'aident en aucune façon. Code correct:
while ((status = NtQuerySystemInformation(....)) == STATUS_INFO_LENGTH_MISMATCH)
En raison de cette erreur, la macro UT_ASSERT ne fonctionnera jamais. En effet, la variable status contient toujours le résultat de la comparaison, c'est-à-dire faux (0) ou vrai (1). Par conséquent, la condition ([0..1]> = 0) est toujours vraie.
Fuite de mémoire potentielle
static enum pocli_ret
pocli_args_obj_root(struct pocli_ctx *ctx, char *in, PMEMoid **oidp)
{
char *input = strdup(in);
if (!input)
return POCLI_ERR_MALLOC;
if (!oidp)
return POCLI_ERR_PARS;
....
}
Avertissement PVS-Studio: V773 [CWE-401] La fonction a été quittée sans relâcher le pointeur «d'entrée». Une fuite de mémoire est possible. pmemobjcli.c 238
Si oidp s'avère être un pointeur nul, la copie de la chaîne créée en appelant la fonction strdup sera perdue . Il serait préférable de reporter la vérification avant d'allouer de la mémoire:
static enum pocli_ret
pocli_args_obj_root(struct pocli_ctx *ctx, char *in, PMEMoid **oidp)
{
if (!oidp)
return POCLI_ERR_PARS;
char *input = strdup(in);
if (!input)
return POCLI_ERR_MALLOC;
....
}
Ou, vous pouvez explicitement libérer la mémoire:
static enum pocli_ret
pocli_args_obj_root(struct pocli_ctx *ctx, char *in, PMEMoid **oidp)
{
char *input = strdup(in);
if (!input)
return POCLI_ERR_MALLOC;
if (!oidp)
{
free(input);
return POCLI_ERR_PARS;
}
....
}
Débordement potentiel
typedef long long os_off_t;
void
do_memcpy(...., int dest_off, ....., size_t mapped_len, .....)
{
....
LSEEK(fd, (os_off_t)(dest_off + (int)(mapped_len / 2)), SEEK_SET);
....
}
Avertissement PVS-Studio: V1028 [CWE-190] Dépassement possible. Envisagez de transtyper des opérandes, pas le résultat. memcpy_common.c 62 Cela n'a aucun sens de transtyper
explicitement le résultat de l'addition au type os_off_t . Premièrement, il ne protège pas contre les débordements potentiels qui peuvent se produire lors de l'ajout de deux valeurs int . Deuxièmement, le résultat de l'ajout aurait été parfaitement implicitement étendu au type os_off_t . La conversion de type explicite est simplement redondante.
Je pense qu'il serait plus correct d'écrire comme ceci:
LSEEK(fd, dest_off + (os_off_t)(mapped_len) / 2, SEEK_SET);
Ici, une valeur non signée de type size_t est convertie en une valeur signée (afin qu'il n'y ait pas d'avertissement du compilateur). Et en même temps, il n'y aura certainement pas de débordement lors de l'ajout.
Protection anti-débordement incorrecte
static DWORD
get_rel_wait(const struct timespec *abstime)
{
struct __timeb64 t;
_ftime64_s(&t);
time_t now_ms = t.time * 1000 + t.millitm;
time_t ms = (time_t)(abstime->tv_sec * 1000 +
abstime->tv_nsec / 1000000);
DWORD rel_wait = (DWORD)(ms - now_ms);
return rel_wait < 0 ? 0 : rel_wait;
}
Avertissement PVS-Studio: V547 [CWE-570] L'expression «rel_wait <0» est toujours fausse. La valeur du type non signé n'est jamais <0. os_thread_windows.c 359
Je ne suis pas très clair sur la situation contre laquelle la vérification doit protéger, mais cela ne fonctionne pas de toute façon. La variable rel_wait est de type DWORD non signé . Cela signifie que la comparaison rel_wait <0 n'a pas de sens, car le résultat est toujours vrai.
Manque de vérification que la mémoire a été allouée avec succès
La vérification que la mémoire est allouée se fait à l'aide de macros d' assert , qui ne font rien si la version Release de l'application est compilée. On peut donc dire qu'il n'y a pas de traitement de la situation où les fonctions malloc retournent NULL . Exemple:
static void
remove_extra_node(TOID(struct tree_map_node) *node)
{
....
unsigned char *new_key = (unsigned char *)malloc(new_key_size);
assert(new_key != NULL);
memcpy(new_key, D_RO(tmp)->key, D_RO(tmp)->key_size);
....
}
Avertissement PVS-Studio: V575 [CWE-628] Le pointeur nul potentiel est passé dans la fonction 'memcpy'. Inspectez le premier argument. Vérifiez les lignes: 340, 338. rtree_map.c 340
Ailleurs, il n'y a même pas d' assert :
static void
calc_pi_mt(void)
{
....
HANDLE *workers = (HANDLE *) malloc(sizeof(HANDLE) * pending);
for (i = 0; i < pending; ++i) {
workers[i] = CreateThread(NULL, 0, calc_pi,
&tasks[i], 0, NULL);
if (workers[i] == NULL)
break;
}
....
}
Avertissement PVS-Studio: V522 [CWE-690] Il peut y avoir déréférencement d'un pointeur nul potentiel «workers». Lignes de contrôle: 126, 124. pi.c 126
J'ai compté au moins 37 fragments de code. Je ne vois donc aucune raison de les énumérer tous dans l'article.
À première vue, le manque de vérifications peut être considéré comme une simple négligence et dire qu'il s'agit d'un code avec une odeur. Je ne suis pas d'accord avec cette position. Les programmeurs sous-estiment le danger de manquer de tels contrôles. Un pointeur nul ne se manifeste pas nécessairement immédiatement comme un blocage du programme lors de la tentative de déréférencement. Les conséquences peuvent être plus bizarres et dangereuses, en particulier dans les programmes multithreads. Pour comprendre plus en détail ce qui se passe et pourquoi des vérifications sont nécessaires, je recommande vivement à tout le monde de lire l'article "Pourquoi est-il important de vérifier ce que retourne malloc ».
Code d'odeur
Double appel CloseHandle
static void
prepare_map(struct pmem2_map **map_ptr,
struct pmem2_config *cfg, struct pmem2_source *src)
{
....
HANDLE mh = CreateFileMapping(....);
....
UT_ASSERTne(CloseHandle(mh), 0);
....
}
Avertissement PVS-Studio: V586 [CWE-675] La fonction 'CloseHandle' est appelée deux fois pour la désallocation de la même ressource. pmem2_map.c 76
En regardant ce code et l'avertissement PVS-Studio, il est clair que rien n'est clair. Où puis-je appeler CloseHandle à nouveau ? Pour trouver la réponse, regardons l'implémentation de la macro UT_ASSERTne .
#define UT_ASSERTne(lhs, rhs)\
do {\
/* See comment in UT_ASSERT. */\
if (__builtin_constant_p(lhs) && __builtin_constant_p(rhs))\
UT_ASSERT_COMPILE_ERROR_ON((lhs) != (rhs));\
UT_ASSERTne_rt(lhs, rhs);\
} while (0)
Ce n'est pas devenu beaucoup plus clair. Qu'est-ce que UT_ASSERT_COMPILE_ERROR_ON ? Qu'est - ce que UT_ASSERTne_rt ?
Je ne vais pas encombrer l'article avec une description de chaque macro et tourmenter le lecteur, l'obligeant à insérer une macros dans d'autres dans sa tête. Voyons tout de suite la version finale du code ouvert extrait du fichier prétraité.
do {
if (0 && 0) (void)((CloseHandle(mh)) != (0));
((void)(((CloseHandle(mh)) != (0)) ||
(ut_fatal(".....", 76, __FUNCTION__, "......: %s (0x%llx) != %s (0x%llx)",
"CloseHandle(mh)", (unsigned long long)(CloseHandle(mh)), "0",
(unsigned long long)(0)), 0))); } while (0);
Supprimons la condition toujours fausse (0 && 0) et, en général, tout ce qui n'est pas pertinent. Il s'avère:
((void)(((CloseHandle(mh)) != (0)) ||
(ut_fatal(...., "assertion failure: %s (0x%llx) != %s (0x%llx)",
....., (unsigned long long)(CloseHandle(mh)), .... ), 0)));
La poignée est fermée. Si une erreur se produit, un message de débogage est généré et, afin d'obtenir à nouveau le code d'erreur, CloseHandle est appelé pour le même handle incorrect.
Erreurs, en quelque sorte, et non. Étant donné que le handle n'est pas valide, il est normal que la fonction CloseHandle soit appelée deux fois pour cela . Cependant, ce code est inodore. Il serait plus correct d'un point de vue idéologique d'appeler la fonction une seule fois et d'enregistrer l'état qu'elle a renvoyé, de sorte que, si nécessaire, affiche sa valeur dans le message.
Incohérence de l'interface d'implémentation (suppression de const)
static int
status_push(PMEMpoolcheck *ppc, struct check_status *st, uint32_t question)
{
....
} else {
status_msg_info_and_question(st->msg); // <=
st->question = question;
ppc->result = CHECK_RESULT_ASK_QUESTIONS;
st->answer = PMEMPOOL_CHECK_ANSWER_EMPTY;
PMDK_TAILQ_INSERT_TAIL(&ppc->data->questions, st, next);
}
....
}
L'analyseur affiche le message: V530 [CWE-252] La valeur de retour de la fonction 'status_msg_info_and_question' doit être utilisée. check_util.c 293
La raison en est que la fonction status_msg_info_and_question , du point de vue de l'analyseur, ne change pas l'état des objets qui lui sont extérieurs, y compris la chaîne constante transmise. Ceux. la fonction calcule simplement quelque chose et renvoie le résultat. Et si tel est le cas, il est étrange de ne pas utiliser le résultat renvoyé par cette fonction. Et, bien que l'analyseur se trompe cette fois, il pointe vers un code avec une odeur. Voyons comment fonctionne la fonction status_msg_info_and_question appelée .
static inline int
status_msg_info_and_question(const char *msg)
{
char *sep = strchr(msg, MSG_SEPARATOR);
if (sep) {
*sep = ' ';
return 0;
}
return -1;
}
Lors de l'appel de la fonction strchr , une suppression implicite de la constante se produit. Le fait est qu'en C il est déclaré comme ceci:
char * strchr ( const char *, int );
Pas la meilleure solution. Mais le langage C est ce qu'il est :).
L'analyseur est devenu confus et n'a pas compris que la chaîne transmise changeait réellement. Si tel est le cas, la valeur de retour n'est pas la chose la plus importante et vous ne pouvez pas l'utiliser.
Cependant, bien que l'analyseur soit confus, il pointe vers un code avec une odeur. Ce qui déroute l'analyseur peut également dérouter la personne qui gère le code. Il serait préférable de déclarer la fonction plus honnêtement en supprimant const :
static inline int
status_msg_info_and_question(char *msg)
{
char *sep = strchr(msg, MSG_SEPARATOR);
if (sep) {
*sep = ' ';
return 0;
}
return -1;
}
Les intentions sont donc immédiatement plus claires et l'analyseur sera silencieux.
Code trop compliqué
static struct memory_block
heap_coalesce(struct palloc_heap *heap,
const struct memory_block *blocks[], int n)
{
struct memory_block ret = MEMORY_BLOCK_NONE;
const struct memory_block *b = NULL;
ret.size_idx = 0;
for (int i = 0; i < n; ++i) {
if (blocks[i] == NULL)
continue;
b = b ? b : blocks[i];
ret.size_idx += blocks[i] ? blocks[i]->size_idx : 0;
}
....
}
Avertissement PVS-Studio: V547 [CWE-571] L'expression «blocs [i]» est toujours vraie. heap.c 1054
Si bloque [i] == NULL , alors l'instruction continue sera déclenchée et la boucle démarrera l'itération suivante. Par conséquent, revérifier les blocs d' éléments [i] n'a pas de sens et l'opérateur ternaire est superflu. Le code peut être simplifié:
....
for (int i = 0; i < n; ++i) {
if (blocks[i] == NULL)
continue;
b = b ? b : blocks[i];
ret.size_idx += blocks[i]->size_idx;
}
....
Utilisation suspecte d'un pointeur nul
void win_mmap_fini(void)
{
....
if (mt->BaseAddress != NULL)
UnmapViewOfFile(mt->BaseAddress);
size_t release_size =
(char *)mt->EndAddress - (char *)mt->BaseAddress;
void *release_addr = (char *)mt->BaseAddress + mt->FileLen;
mmap_unreserve(release_addr, release_size - mt->FileLen);
....
}
Avertissement PVS-Studio: V1004 [CWE-119] Le pointeur '(char *) mt-> BaseAddress' a été utilisé de manière non sécurisée après avoir été vérifié par rapport à nullptr. Lignes de contrôle: 226, 235. win_mmap.c 235
Le pointeur mt-> BaseAddress peut être nul, comme en témoigne la vérification:
if (mt->BaseAddress != NULL)
Cependant, en dessous, ce pointeur est déjà utilisé dans les opérations arithmétiques sans vérification. Par exemple, ici:
size_t release_size =
(char *)mt->EndAddress - (char *)mt->BaseAddress;
Une grande valeur entière sera reçue, qui est en fait la valeur du pointeur mt-> EndAddress . Ce n'est peut-être pas un bogue, mais tout cela semble très suspect, et il me semble que le code devrait être revérifié. L'odeur réside dans le fait que le code est incompréhensible et manque clairement de commentaires explicatifs.
Noms courts des variables globales
Je crois que le code sent s'il contient des variables globales avec des noms courts. Il est facile de sceller et d'utiliser accidentellement non pas une variable locale, mais une variable globale dans une fonction. Exemple:
static struct critnib *c;
Avertissements PVS-Studio pour ces variables:
- V707 Donner des noms courts aux variables globales est considéré comme une mauvaise pratique. Il est suggéré de renommer la variable «ri». map.c 131
- V707 Donner des noms courts aux variables globales est considéré comme une mauvaise pratique. Il est suggéré de renommer la variable «c». obj_critnib_mt.c 56
- V707 Donner des noms courts aux variables globales est considéré comme une mauvaise pratique. Il est suggéré de renommer la variable «Id». obj_list.h 68
- V707 Donner des noms courts aux variables globales est considéré comme une mauvaise pratique. Il est suggéré de renommer la variable «Id». obj_list.c 34
Étrange
Le code le plus étrange que j'ai rencontré est dans la fonction do_memmove . L'analyseur a donné deux points positifs, qui indiquent soit des erreurs très graves, soit que je ne comprends tout simplement pas ce que je veux dire. Le code étant très étrange, j'ai décidé de prendre en compte les avertissements émis dans une section distincte de l'article. Le premier avertissement est donc émis ici.
void
do_memmove(char *dst, char *src, const char *file_name,
size_t dest_off, size_t src_off, size_t bytes,
memmove_fn fn, unsigned flags, persist_fn persist)
{
....
/* do the same using regular memmove and verify that buffers match */
memmove(dstshadow + dest_off, dstshadow + dest_off, bytes / 2);
verify_contents(file_name, 0, dstshadow, dst, bytes);
verify_contents(file_name, 1, srcshadow, src, bytes);
....
}
Avertissement PVS-Studio: V549 [CWE-688] Le premier argument de la fonction 'memmove' est égal au deuxième argument. memmove_common.c 71
Notez que le premier et le second arguments de la fonction sont identiques. Ainsi, la fonction ne fait rien en fait. Quelles options me viennent à l'esprit:
- Je voulais "toucher" le bloc mémoire. Mais cela se produira-t-il en réalité? Le compilateur d'optimisation supprimera-t-il le code qui copie le bloc de mémoire sur lui-même?
- Il s'agit d'une sorte de test unitaire pour la fonction memmove .
- Le code contient une faute de frappe.
Et voici un extrait tout aussi étrange dans la même fonction:
void
do_memmove(char *dst, char *src, const char *file_name,
size_t dest_off, size_t src_off, size_t bytes,
memmove_fn fn, unsigned flags, persist_fn persist)
{
....
/* do the same using regular memmove and verify that buffers match */
memmove(dstshadow + dest_off, srcshadow + src_off, 0);
verify_contents(file_name, 2, dstshadow, dst, bytes);
verify_contents(file_name, 3, srcshadow, src, bytes);
....
}
Avertissement PVS-Studio: V575 [CWE-628] La fonction 'memmove' traite les éléments '0'. Inspectez le troisième argument. memmove_common.c 82
La fonction déplace de 0 octet. Qu'Est-ce que c'est? Test de l'unité? Faute de frappe?
Pour moi, ce code est incompréhensible et étrange.
Pourquoi utiliser des analyseurs de code?
Il peut sembler que, puisque peu d'erreurs ont été trouvées, l'introduction de l'analyseur dans le processus de développement de code est déraisonnable. Mais l'intérêt d'utiliser des outils d'analyse statique n'est pas dans les contrôles ponctuels, mais dans la détection régulière d'erreurs au stade de l'écriture du code. Sinon, ces erreurs sont détectées de manière plus coûteuse et plus lente (débogage, test, avis des utilisateurs, etc.). Cette idée est décrite plus en détail dans l'article « Erreurs que l'analyse de code statique ne peut pas trouver, car il n'est pas utilisé », que je recommande de prendre connaissance. Et puis venez sur notre site pour télécharger et essayer PVS-Studio pour vérifier vos projets.
Merci de votre attention!
Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien de traduction: Andrey Karpov. Analyse de code statique de la collection de bibliothèques PMDK par Intel et erreurs qui ne sont pas des erreurs réelles .