Controllo di QEMU utilizzando PVS-Studio

Controllo di QEMU utilizzando PVS-Studio

QEMU è un'applicazione piuttosto nota per l'emulazione. L'analisi statica può aiutare gli sviluppatori di progetti complessi come QEMU a rilevare gli errori nelle fasi iniziali e, in generale, a migliorare la qualità e l'affidabilità di un progetto. In questo articolo, verificheremo il codice sorgente dell'applicazione QEMU per potenziali vulnerabilità ed errori utilizzando lo strumento di analisi statica PVS-Studio.

QEMU è un software gratuito progettato per emulare l'hardware di varie piattaforme. Consente di eseguire applicazioni e sistemi operativi su piattaforme hardware diverse da quelle di destinazione. Ad esempio, è possibile eseguire un'applicazione scritta per MIPS per l'architettura x86. QEMU supporta anche l'emulazione di varie periferiche, come schede video, usb, ecc. Il progetto è piuttosto complesso e degno di attenzione. Tali progetti sono interessanti in termini di analisi statica, quindi abbiamo deciso di scansionarne il codice utilizzando PVS-Studio.

Informazioni sull'analisi

Il codice sorgente del progetto può essere ottenuto dal mirror su github. Il progetto è abbastanza grande e può essere compilato per varie piattaforme. Per un controllo più semplice del codice, utilizziamo il sistema di monitoraggio della compilazione PVS-Studio. Questo sistema è progettato per un'integrazione molto semplice dell'analisi statica in quasi tutte le piattaforme di build. Il sistema si basa sul monitoraggio delle chiamate del compilatore durante la compilazione e consente di raccogliere tutte le informazioni per l'analisi successiva dei file. In altre parole, eseguiamo semplicemente la build, PVS-Studio raccoglie le informazioni necessarie e quindi eseguiamo l'analisi:tutto è semplice. I dettagli possono essere trovati dal link sopra.

Dopo il controllo, l'analizzatore ha riscontrato molti potenziali problemi. Per quanto riguarda la diagnostica relativa all'analisi generale, abbiamo:1940 diagnostiche di Alto livello, 1996 - Medio livello, 9596 - Basso livello. Dopo aver visionato tutte le avvertenze, ho deciso di concentrarmi sulla diagnostica dell'Alto livello di certezza. C'erano parecchi avvisi di questo tipo (1940), ma la maggior parte di essi sono dello stesso tipo o sono associati all'uso ripetuto di una macro sospetta. Ad esempio, diamo un'occhiata a g_new macro.

#define g_new(struct_type, n_structs)
                        _G_NEW (struct_type, n_structs, malloc)

#define _G_NEW(struct_type, n_structs, func)       \
  (struct_type *) (G_GNUC_EXTENSION ({             \
    gsize __n = (gsize) (n_structs);               \
    gsize __s = sizeof (struct_type);              \
    gpointer __p;                                  \
    if (__s == 1)                                  \
      __p = g_##func (__n);                        \
    else if (__builtin_constant_p (__n) &&         \
             (__s == 0 || __n <= G_MAXSIZE / __s)) \
      __p = g_##func (__n * __s);                  \
    else                                           \
      __p = g_##func##_n (__n, __s);               \
    __p;                                           \
  }))

Per ogni utilizzo di questa macro, l'analizzatore emette l'avviso V773 (l'ambito di visibilità del puntatore '__p' è stato chiuso senza rilasciare la memoria. È possibile una perdita di memoria). Il g_new la macro è definita nella libreria glib, usa _g_new macro, e questa macro a sua volta usa un altro G_GNUC_EXTENSION macro che dice al compilatore GCC di saltare gli avvisi sul codice non standard. È questo codice non standard che attiva l'avviso dell'analizzatore. Basta guardare come la penultima riga di codice. In effetti, la macro è valida. C'erano 848 avvisi di questo tipo, il che significa che quasi la metà degli avvisi si verifica in un solo punto del codice.

Tutti questi avvisi non necessari possono essere facilmente rimossi utilizzando le impostazioni dell'analizzatore. Tuttavia, questo caso particolare, che si è verificato durante la stesura dell'articolo, è il motivo per cui il nostro team ha affinato leggermente la logica dell'analizzatore per tali situazioni.

Pertanto, un gran numero di avvisi non sempre indica una scarsa qualità del codice. Tuttavia, ci sono alcuni posti davvero sospetti. Bene, passiamo alla revisione degli avvisi.

Avviso N1

V517 È stato rilevato l'uso del pattern 'if (A) {...} else if (A) {...}'. C'è una probabilità di presenza di un errore logico. Linee di controllo:2395, 2397. megasas.c 2395

#define MEGASAS_MAX_SGE 128             /* Firmware limit */
....
static void megasas_scsi_realize(PCIDevice *dev, Error **errp)
{
  ....
  if (s->fw_sge >= MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE) {
    ....
  } else if (s->fw_sge >= 128 - MFI_PASS_FRAME_SIZE) {
    ....
  }
  ....
}

Qualsiasi uso di numeri "magici" nel codice è sempre sospetto. Ci sono due condizioni qui e, a prima vista, sembrano diverse, ma se guardi il valore di MEGASAS_MAX_SGE macro, si scopre che le condizioni si duplicano a vicenda. Molto probabilmente, c'è un errore di battitura e dovrebbe essere scritto un numero diverso invece di 128. Certo, questo è il problema con tutti i numeri "magici", si possono facilmente digitare in modo errato. L'uso di macro e costanti aiuterà molto uno sviluppatore in questo caso.

Avviso N2

V523 L'istruzione 'then' è equivalente all'istruzione 'else'. cp0_helper.c 383

target_ulong helper_mftc0_cause(CPUMIPSState *env)
{
  ....
  CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);

  if (other_tc == other->current_tc) {
    tccause = other->CP0_Cause;
  } else {
    tccause = other->CP0_Cause;
  }
  ....
}

Nel codice sopra, quindi e altro organi del se affermazione sono identiche. Molto probabilmente, è copia-incolla. L'autore ha appena copiato il corpo di allora ramo e ho dimenticato di aggiustarlo. Per quanto posso vedere, env avrebbe dovuto essere utilizzato al posto di altro oggetto. Riparare questo luogo sospetto può assomigliare a questo:

if (other_tc == other->current_tc) {
  tccause = other->CP0_Cause;
} else {
  tccause = env->CP0_Cause;
}

Solo gli sviluppatori di questo codice possono dire chiaramente come dovrebbe essere effettivamente. Un altro frammento simile:

  • V523 L'istruzione 'then' è equivalente all'istruzione 'else'. tradurre.c 641

Avviso N3

V547 L'espressione 'ret <0' è sempre falsa. qcow2-cluster.c 1557

static int handle_dependencies(....)
{
  ....
  if (end <= old_start || start >= old_end) {
    ....
  } else {

    if (bytes == 0 && *m) {
      ....
      return 0;           // <= 3
    }

    if (bytes == 0) {
      ....
      return -EAGAIN;     // <= 4
    }
  ....
  }
  return 0;               // <= 5
}

int qcow2_alloc_cluster_offset(BlockDriverState *bs, ....)
{
  ....
  ret = handle_dependencies(bs, start, &cur_bytes, m);
  if (ret == -EAGAIN) {   // <= 2
    ....
  } else if (ret < 0) {   // <= 1
    ....
  }
}

Qui, l'analizzatore ha rilevato che la condizione (commento 1) non sarà mai soddisfatta. Il valore di ret viene inizializzata dal risultato dell'esecuzione di handle_dependencies funzione. Questa funzione restituisce solo 0 o -EAGAIN (commenti 3, 4, 5). Appena sopra, nella prima condizione, abbiamo verificato il valore di ret per -EAGAIN (commento 2), quindi il risultato dell'esecuzione dell'espressione ret <0 sarà sempre falso. È possibile che le handle_dependencies funzione utilizzata per restituire altri valori, ma poi, a seguito del refactoring, ad esempio, il comportamento è cambiato. Qui non resta che completare il refactoring. Avvisi simili:

  • L'espressione V547 è sempre falsa. qcow2.c 1070
  • L'espressione V547 's->state !=MIGRATION_STATUS_COLO' è sempre falsa. colo.c 595
  • L'espressione V547 's->metadata_entries.present &0x20' è sempre falsa. vhdx.c 769

Avviso N4

V557 È possibile il sovraccarico dell'array. La funzione 'dwc2_glbreg_read' elabora il valore '[0..63]'. Esamina il terzo argomento. Righe di controllo:667, 1040. hcd-dwc2.c 667

#define HSOTG_REG(x) (x)                                             // <= 5
....
struct DWC2State {
  ....
#define DWC2_GLBREG_SIZE    0x70
  uint32_t glbreg[DWC2_GLBREG_SIZE / sizeof(uint32_t)];              // <= 1
  ....
}
....
static uint64_t dwc2_glbreg_read(void *ptr, hwaddr addr, int index,
                                 unsigned size)
{
  ....
  val = s->glbreg[index];                                            // <= 2
  ....
}
static uint64_t dwc2_hsotg_read(void *ptr, hwaddr addr, unsigned size)
{
  ....
  switch (addr) {
    case HSOTG_REG(0x000) ... HSOTG_REG(0x0fc):                      // <= 4
        val = dwc2_glbreg_read(ptr, addr,
                              (addr - HSOTG_REG(0x000)) >> 2, size); // <= 3
    ....
  }
  ....
}

Questo codice presenta un potenziale problema:un indice al di fuori dei limiti dell'array. Lo stato DWC2 struttura definisce un glbreg matrice composta da 28 elementi (commento 1). Nella dwc2_glbreg_read funzione, il nostro array è accessibile da index (commento 2). Ora nota che la funzione dwc2_glbreg_read viene passata l'espressione (addr - HSOTG_REG(0x000)) >> 2 (commento 3) come indice, che può assumere un valore nell'intervallo [0..63]. Per esserne sicuri, presta attenzione ai commenti 4 e 5. Forse, l'intervallo di valori dal commento 4 deve essere corretto.

Avvisi più simili:

  • È possibile il sovraccarico dell'array V557. La funzione 'dwc2_hreg0_read' elabora il valore '[0..63]'. Esamina il terzo argomento. Righe di controllo:814, 1050. hcd-dwc2.c 814
  • È possibile il sovraccarico dell'array V557. La funzione 'dwc2_hreg1_read' elabora il valore '[0..191]'. Esamina il terzo argomento. Righe di controllo:927, 1053. hcd-dwc2.c 927
  • È possibile il sovraccarico dell'array V557. La funzione 'dwc2_pcgreg_read' elabora il valore '[0..127]'. Esamina il terzo argomento. Righe di controllo:1012, 1060. hcd-dwc2.c 1012

Avviso N5

V575 La funzione 'strerror_s' elabora elementi '0'. Esamina il secondo argomento. comandi-win32.c 1642

void qmp_guest_set_time(bool has_time, int64_t time_ns, 
                        Error **errp)
{
  ....
  if (GetLastError() != 0) {
    strerror_s((LPTSTR) & msg_buffer, 0, errno);
    ....
  }
}

Gli strerror_s La funzione restituisce la descrizione testuale del codice di errore del sistema. La sua firma si presenta così:

errno_t strerror_s( char *buf, rsize_t bufsz, errno_t errnum );

Il primo parametro è un puntatore al buffer in cui verrà copiata la descrizione del testo, il secondo parametro è la dimensione del buffer e il terzo parametro - il codice di errore. Il codice passa 0 come dimensione del buffer, che è chiaramente un valore errato. A proposito, è possibile scoprire in anticipo quanti byte allocare:basta chiamare strerrorlen_s , che restituisce la lunghezza della descrizione del testo dell'errore. Questo valore può essere utilizzato per allocare un buffer di dimensioni sufficienti.

Avviso N6

V595 Il puntatore 'blen2p' è stato utilizzato prima di essere verificato rispetto a nullptr. Righe di controllo:103, 106. dsound_template.h 103

static int glue (
    ....
    DWORD *blen1p,
    DWORD *blen2p,
    int entire,
    dsound *s
    )
{
  ....
  dolog("DirectSound returned misaligned buffer %ld %ld\n",
        *blen1p, *blen2p);                         // <= 1
  glue(.... p2p ? *p2p : NULL, *blen1p,
                            blen2p ? *blen2p : 0); // <= 2
....
}

In questo codice, il valore di blen2p viene prima utilizzato l'argomento (commento 1), quindi verificato per nullptr (commento 2). Questo luogo estremamente sospetto sembra come se ci si fosse appena dimenticati di inserire un assegno prima del primo utilizzo (commento 1). Come opzione di correzione, puoi semplicemente aggiungere un segno di spunta:

dolog("DirectSound returned misaligned buffer %ld %ld\n",
      *blen1p, blen2p ? *blen2p : 0);

C'è anche una domanda su blen1p discussione. Probabilmente può anche essere un puntatore nullo e dovrai anche aggiungere un segno di spunta qui. Alcuni altri avvisi simili:

  • V595 Il puntatore 'ref' è stato utilizzato prima di essere verificato rispetto a nullptr. Righe di controllo:2191, 2193. uri.c 2191
  • V595 Il puntatore 'cmdline' è stato utilizzato prima che fosse verificato rispetto a nullptr. Righe di controllo:420, 425. qemu-io.c 420
  • V595 Il puntatore 'dp' è stato utilizzato prima di essere verificato rispetto a nullptr. Righe di controllo:288, 294. onenand.c 288
  • V595 Il puntatore 'omap_lcd' è stato utilizzato prima che fosse verificato rispetto a nullptr. Righe di controllo:81, 87. omap_lcdc.c 81

Avviso N7

V597 Il compilatore potrebbe eliminare la chiamata di funzione 'memset', che viene utilizzata per svuotare l'oggetto 'op_info'. La funzione RtlSecureZeroMemory() dovrebbe essere utilizzata per cancellare i dati privati. virtio-crypto.c 354

static void virtio_crypto_free_request(VirtIOCryptoReq *req)
{
  if (req) {
    if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
      ....
      /* Zeroize and free request data structure */
      memset(op_info, 0, sizeof(*op_info) + max_len); // <= 1
      g_free(op_info);
    }
    g_free(req);
  }
}

In questo frammento di codice, il memset viene chiamata la funzione per op_info oggetto (commento 1). Successivamente, op_info viene immediatamente cancellato. In altre parole, dopo la cancellazione, questo oggetto non viene modificato da nessun'altra parte. Questo è esattamente il caso in cui il compilatore può eliminare il memset chiamata durante l'ottimizzazione. Per evitare questo potenziale comportamento, è possibile utilizzare funzioni speciali che il compilatore non elimina mai. Vedi anche l'articolo "Cancellazione sicura dei dati privati".

Avviso N8

V610 Comportamento non specificato. Controllare l'operatore di turno '>>'. L'operando sinistro è negativo ("numero" =[-32768..2147483647]). cris.c 2111

static void
print_with_operands (const struct cris_opcode *opcodep,
         unsigned int insn,
         unsigned char *buffer,
         bfd_vma addr,
         disassemble_info *info,
         const struct cris_opcode *prefix_opcodep,
         unsigned int prefix_insn,
         unsigned char *prefix_buffer,
         bfd_boolean with_reg_prefix)
{
  ....
  int32_t number;
  ....
  if (signedp && number > 127)
    number -= 256;            // <= 1
  ....
  if (signedp && number > 32767)
    number -= 65536;          // <= 2
  ....
  unsigned int highbyte = (number >> 24) & 0xff;
  ....
}

Dal numero la variabile può avere un valore negativo, uno spostamento bit per bit a destra è un comportamento non specificato. Per assicurarti che la variabile in questione possa assumere un valore negativo, guarda i commenti 1 e 2. Per eliminare le differenze nel comportamento del tuo codice su piattaforme diverse, dovresti evitare questi casi.

Altri avvisi:

  • V610 Comportamento non definito. Controllare l'operatore di turno '<<'. L'operando sinistro è negativo ('(hclk_div - 1)' =[-1..15]). aspeed_smc.c 1041
  • V610 Comportamento non definito. Controllare l'operatore di turno '<<'. L'operando sinistro '(target_long) - 1' è negativo. exec-vary.c 99
  • V610 Comportamento non definito. Controllare l'operatore di turno '<<'. L'operando sinistro è negativo ('hex2nib(words[3][i * 2 + 2])' =[-1..15]). qtest.c 561

Esistono anche diversi avvisi dello stesso tipo, la differenza è che l'operando sinistro è -1 .

V610 Comportamento indefinito. Controllare l'operatore di turno '<<'. L'operando sinistro '-1' è negativo. hppa.c 2702

int print_insn_hppa (bfd_vma memaddr, disassemble_info *info)
{
  ....
  disp = (-1 << 10) | imm10;
  ....
}

Altri avvisi simili:

  • V610 Comportamento non definito. Controllare l'operatore di turno '<<'. L'operando sinistro '-1' è negativo. hppa.c 2718
  • V610 Comportamento non definito. Controllare l'operatore di turno '<<'. L'operando sinistro '-0x8000' è negativo. fmopl.c 1022
  • V610 Comportamento non definito. Controllare l'operatore di turno '<<'. L'operando sinistro '(intptr_t) - 1' è negativo. sve_helper.c 889

Avviso N9

V616 La costante denominata 'TIMER_NONE' con il valore 0 viene utilizzata nell'operazione bit per bit. sys_helper.c 179

#define HELPER(name) ....

enum {
  TIMER_NONE = (0 << 30),        // <= 1
  ....
}

void HELPER(mtspr)(CPUOpenRISCState *env, ....)
{
  ....
  if (env->ttmr & TIMER_NONE) {  // <= 2
    ....
  }
}

Puoi facilmente assicurarti che il valore della macro TIMER_NONE sia zero (commento 1). Questa macro viene quindi utilizzata in un'operazione bit per bit, il cui risultato è sempre 0. Di conseguenza, il corpo dell'istruzione condizionale if if (env->ttmr &TIMER_NONE) non verrà mai eseguito.

Avviso N10

V629 Considerare di esaminare l'espressione 'n <<9'. Spostamento di bit del valore a 32 bit con successiva espansione al tipo a 64 bit. qemu-img.c 1839

#define BDRV_SECTOR_BITS   9
static int coroutine_fn convert_co_read(ImgConvertState *s, 
                  int64_t sector_num, int nb_sectors, uint8_t *buf)
{
  uint64_t single_read_until = 0;
  int n;
  ....
  while (nb_sectors > 0) {
    ....
    uint64_t offset;
    ....
    single_read_until = offset + (n << BDRV_SECTOR_BITS);
    ....
  }
  ....
}

In questo frammento di codice, n la variabile del tipo con segno a 32 bit viene spostata, quindi questo risultato con segno a 32 bit viene espanso a un tipo con segno a 64 bit. Dopodiché questo risultato viene aggiunto all'offset variabile a 64 bit senza segno come tipo senza segno. Assumiamo che al momento dell'esecuzione dell'espressione, la variabile n ha alcuni 9 bit alti significativi. Eseguiamo un'operazione di spostamento a 9 bit (BDRV_SECTOR_BITS ), e questo, a sua volta, è un comportamento indefinito, quindi possiamo ottenere il bit impostato nell'ordine più alto come risultato. Lascia che ti ricordi rapidamente che questo bit nel tipo firmato è responsabile del segno, quindi il risultato può diventare negativo. Poiché la variabile n è di tipo firmato, l'estensione terrà conto del segno. Inoltre, il risultato viene aggiunto all'offset variabile. Da queste considerazioni, non è difficile vedere che il risultato dell'esecuzione di un'espressione può differire da quello previsto. Una possibile soluzione è sostituire il tipo di n variabile con un tipo senza segno a 64 bit, ovvero uint64_t .

Ecco altri avvisi simili:

  • V629 Considerare di esaminare l'espressione '1 <
  • V629 Considerare di esaminare l'espressione 's->cluster_size <<3'. Spostamento di bit del valore a 32 bit con successiva espansione al tipo a 64 bit. qcow2-bitmap.c 283
  • V629 Considerare di esaminare l'espressione 'i <cluster_bits'. Spostamento di bit del valore a 32 bit con successiva espansione al tipo a 64 bit. qcow2-cluster.c 983
  • V629 Considerare di esaminare l'espressione. Spostamento di bit del valore a 32 bit con successiva espansione al tipo a 64 bit. vhdx.c 1145
  • V629 Considerare di esaminare l'espressione 'delta <<2'. Spostamento di bit del valore a 32 bit con successiva espansione al tipo a 64 bit. mips.c 4341

Avviso N11

V634 La priorità dell'operazione '*' è maggiore di quella dell'operazione '<<'. È possibile che le parentesi debbano essere utilizzate nell'espressione. nand.c 310

static void nand_command(NANDFlashState *s)
{
  ....
  s->addr &= (1ull << s->addrlen * 8) - 1;
  ....
}

Questo frammento è semplicemente sospetto. Non è chiaro cosa volesse fare prima lo sviluppatore:spostamento o moltiplicazione. Anche se non ci sono errori qui, è comunque necessario guardare di nuovo il codice e inserire correttamente le parentesi. Questo è solo uno dei posti che gli sviluppatori dovrebbero controllare per assicurarsi che il loro algoritmo sia corretto. Altri frammenti simili:

  • V634 La priorità dell'operazione '*' è maggiore di quella dell'operazione '<<'. È possibile che le parentesi debbano essere utilizzate nell'espressione. exynos4210_mct.c 449
  • V634 La priorità dell'operazione '*' è maggiore di quella dell'operazione '<<'. È possibile che le parentesi debbano essere utilizzate nell'espressione. exynos4210_mct.c 1235
  • V634 La priorità dell'operazione '*' è maggiore di quella dell'operazione '<<'. È possibile che le parentesi debbano essere utilizzate nell'espressione. exynos4210_mct.c 1264

Avviso N12

V646 Considerare di ispezionare la logica dell'applicazione. È possibile che manchi la parola chiave "altro". pl181.c 400

static void pl181_write(void *opaque, hwaddr offset,
                        uint64_t value, unsigned size)
{
  ....
  if (s->cmd & PL181_CMD_ENABLE) {
    if (s->cmd & PL181_CMD_INTERRUPT) {
      ....
    } if (s->cmd & PL181_CMD_PENDING) { // <= else if
      ....
    } else {
      ....
    }
    ....
  }
  ....
}

In questo codice, a giudicare dalla formattazione, viene utilizzato else if invece di se sembra molto attraente. Forse l'autore ha dimenticato di aggiungere altro qui. In questo modo, il frammento può essere riparato come segue:

} else if (s->cmd & PL181_CMD_PENDING) { // <= else if

Tuttavia, è possibile che questo codice vada bene e che sia presente una formattazione errata del testo del programma, che crea confusione. Quindi il codice corretto potrebbe essere simile a questo:

if (s->cmd & PL181_CMD_INTERRUPT) {
  ....
}
if (s->cmd & PL181_CMD_PENDING) { // <= if
  ....
} else {
  ....
}

Avviso N13

V773 La funzione è stata abbandonata senza rilasciare il puntatore 'regola'. È possibile una perdita di memoria. blkdebug.c 218

static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
{
  ....
  struct BlkdebugRule *rule;
  ....
  rule = g_malloc0(sizeof(*rule));                   // <= 1
  ....
  if (local_error) {
    error_propagate(errp, local_error);
    return -1;                                       // <= 2
  }
  ....
  /* Add the rule */
  QLIST_INSERT_HEAD(&s->rules[event], rule, next);   // <= 3
  ....
}

In questo codice, la regola l'oggetto viene allocato (commento 1) e aggiunto all'elenco per un uso successivo (commento 3), ma in caso di errore, la funzione ritorna senza eliminare la regola precedentemente creata oggetto (commento 2). L'errore deve solo essere gestito correttamente:si può eliminare l'oggetto creato in precedenza, altrimenti si verificherà una perdita di memoria.

Avviso N14

V781 Il valore dell'indice 'ix' viene verificato dopo che è stato utilizzato. Forse c'è un errore nella logica del programma. uri.c 2110

char *uri_resolve_relative(const char *uri, const char *base)
{
  ....
  ix = pos;
  if ((ref->path[ix] == '/') && (ix > 0)) {
  ....
}

Qui, l'analizzatore ha rilevato un potenziale indice di matrice fuori limite. Innanzitutto, il ref->percorso elemento array viene letto da ix index, quindi ix viene verificata la correttezza (ix> 0 ). La soluzione giusta qui è invertire queste azioni:

if ((ix > 0) && (ref->path[ix] == '/')) {

C'erano diversi posti simili:

  • V781 Il valore dell'indice 'ix' viene verificato dopo che è stato utilizzato. Forse c'è un errore nella logica del programma. uri.c 2112
  • V781 Il valore dell'indice 'offset' viene verificato dopo che è stato utilizzato. Forse c'è un errore nella logica del programma. keymaps.c 125
  • V781 Il valore della variabile 'quality' viene verificato dopo che è stata utilizzata. Forse c'è un errore nella logica del programma. Righe di controllo:326, 335. vnc-enc-tight.c 326
  • V781 Il valore dell'indice 'i' viene verificato dopo che è stato utilizzato. Forse c'è un errore nella logica del programma. mem_helper.c 1929

Avviso N15

V784 La dimensione della maschera di bit è inferiore alla dimensione del primo operando. Ciò causerà la perdita di bit più alti. cadence_gem.c 1486

typedef struct CadenceGEMState {
  ....
  uint32_t regs_ro[CADENCE_GEM_MAXREG];
}
....
static void gem_write(void *opaque, hwaddr offset, uint64_t val,
        unsigned size)
{
  ....
  val &= ~(s->regs_ro[offset]);
  ....
}

Questo codice esegue un'operazione bit per bit con oggetti di tipo diverso. L'operando sinistro è val argomento che ha un tipo senza segno a 64 bit. L'operando destro è il valore ricevuto dell'elemento dell'array s->regs_ro dalla compensazione index, che ha un tipo senza segno a 32 bit. Il risultato dell'operazione nella parte destra (~(s->regs_ro[offset])) è un tipo senza segno a 32 bit. Prima della moltiplicazione bit per bit si espanderà nel tipo a 64 bit con zeri, ovvero, dopo aver valutato l'intera espressione, tutti i bit più alti di val la variabile verrà azzerata. Questi posti sembrano sempre dubbi. Qui possiamo solo consigliare agli sviluppatori di rivedere nuovamente questo codice. Frammenti più simili:

  • V784 La dimensione della maschera di bit è inferiore alla dimensione del primo operando. Ciò causerà la perdita di bit più alti. xlnx-zynq-devcfg.c 199
  • V784 La dimensione della maschera di bit è inferiore alla dimensione del primo operando. Ciò causerà la perdita di bit più alti. soc_dma.c 214
  • V784 La dimensione della maschera di bit è inferiore alla dimensione del primo operando. Ciò causerà la perdita di bit più alti. fpu_helper.c 418

Avviso N16

V1046 Utilizzo non sicuro dei tipi 'bool' e 'unsigned int' insieme nell'operazione '&='. helper.c 10821

static inline uint32_t extract32(uint32_t value, int start, int length);
....
static ARMVAParameters aa32_va_parameters(CPUARMState *env, uint32_t va,
                                          ARMMMUIdx mmu_idx)
{
  ....
  bool epd, hpd;
  ....
  hpd &= extract32(tcr, 6, 1);
}

In questo frammento di codice, viene eseguita un'operazione AND bit per bit con hpd variabile, che ha il bool digitare, e con il risultato dell'esecuzione di extract32 funzione, che è di uint32_t genere. Poiché il valore in bit di una variabile booleana può essere solo 0 o 1, il risultato dell'espressione sarà sempre falso se il bit più basso restituito da extract32 la funzione è zero. Consideriamo questo caso usando l'esempio. Supponiamo che hpd value è true e la funzione restituisce il valore 2. Quindi nella rappresentazione binaria, l'operazione sarà simile a 01 e 10 =0 e il risultato dell'espressione sarà false . Molto probabilmente, il programmatore voleva impostare true value se la funzione restituisce qualcosa di diverso da zero. Apparentemente, è necessario correggere il codice in modo che il risultato dell'esecuzione della funzione venga eseguito il cast su bool digita, ad esempio, in questo modo:

hpd = hpd && (bool)extract32(tcr, 6, 1);

Conclusione

Come puoi vedere, l'analizzatore ha trovato molti posti stravaganti. È possibile che questi potenziali problemi finora non si manifestino in alcun modo, ma la loro presenza non può che preoccupare, poiché sono in grado di rivelarsi nel momento più inaspettato. È meglio visualizzare tutti i luoghi incerti in anticipo e modificarli piuttosto che continuare a correggere un flusso infinito di bug. Ovviamente, per progetti complessi come questo, l'analisi statica può portare notevoli benefici, soprattutto se si organizzano verifiche periodiche del progetto.