
QEMU é um aplicativo de emulação bastante conhecido. A análise estática pode ajudar os desenvolvedores de projetos complexos como o QEMU a detectar erros em um estágio inicial e geralmente melhorar sua qualidade e confiabilidade. Neste artigo, o código-fonte do aplicativo QEMU será verificado em busca de vulnerabilidades e erros potenciais usando a ferramenta de análise estática PVS-Studio.
QEMU é um software livre projetado para emular hardware entre plataformas. Ele permite que você execute aplicativos e sistemas operacionais em plataformas de hardware diferentes do destino, por exemplo, um aplicativo escrito para MIPS para ser executado na arquitetura x86. O QEMU também oferece suporte à emulação de uma variedade de periféricos, como placas de vídeo, usb, etc. O projeto é bastante complexo e digno de atenção, são esses projetos que são de interesse para análise estática, por isso decidiu-se verificar seu código usando PVS-Studio.
Sobre análise
O código-fonte do projeto pode ser obtido em um espelho no github . O projeto é bastante grande e pode ser compilado para várias plataformas. Para facilitar a verificação do código, usaremos o sistema de monitoramento de compilação PVS-Studio . Este sistema foi projetado para uma integração muito fácil de análise estática em quase todas as plataformas de construção. O sistema é baseado no rastreamento de chamadas do compilador durante a construção e permite que você colete todas as informações para análise subsequente dos arquivos. Em outras palavras, apenas iniciamos a construção, o PVS-Studio coleta as informações necessárias e então iniciamos a análise - tudo é simples. Detalhes podem ser encontrados no link acima.
Após a verificação, o analisador encontrou muitos problemas potenciais. Para diagnósticos de uso geral (Análise Geral), foi obtido: 1940 Alto, 1996 Médio, 9596 Baixo. Depois de revisar todos os avisos, decidiu-se focar no diagnóstico para o primeiro nível de confiança (Alto). Muitos desses avisos foram encontrados (1940), mas a maioria dos avisos é do mesmo tipo ou está associada ao uso repetido de uma macro suspeita. Por exemplo, considere a macro g_new .
#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; \
}))
Para cada uso desta macro, o analisador emite o aviso V773 (o escopo de visibilidade do ponteiro '__p' foi encerrado sem liberar a memória. É possível um vazamento de memória). A macro g_new é definida na biblioteca glib, ela usa a macro _G_NEW e esta macro, por sua vez, usa outra macro, G_GNUC_EXTENSION , que informa ao compilador GCC para pular os avisos sobre código não padrão. É este código fora do padrão que causa o alerta do analisador, preste atenção na penúltima linha. Em geral, a macro está funcionando. Foram 848 avisos desse tipo, ou seja, quase metade dos alertas ocorrem em apenas um local do código.
Todos esses avisos desnecessários são fáceis de removerusando as configurações do analisador. No entanto, este caso específico, que encontramos ao escrever este artigo, é o motivo para nossa equipe modificar ligeiramente a lógica do analisador para tais situações.
Portanto, um grande número de avisos nem sempre significa má qualidade do código. No entanto, existem alguns lugares realmente suspeitos. Bem, vamos descer aos avisos.
Aviso N1
V517 O uso do padrão 'if (A) {...} else if (A) {...}' foi detectado. Existe uma probabilidade de presença de erro lógico. Verifique as linhas: 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) {
....
}
....
}
Qualquer uso de números "mágicos" no código é sempre suspeito. Existem duas condições aqui e, à primeira vista, parecem ser diferentes, mas se você observar o valor da macro MEGASAS_MAX_SGE , verá que as condições se duplicam. Provavelmente, há um erro de digitação aqui e, em vez de 128, deveria haver outro número. Claro, esse é o problema de todos os números "mágicos", basta estar lacrado ao usá-los. O uso de macros e constantes ajuda muito o desenvolvedor neste caso.
Aviso N2
V523 A instrução 'then' é equivalente à instrução '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;
}
....
}
No código considerado, os corpos then e else do operador condicional são idênticos. Aqui, provavelmente copiar e colar. Basta copiar o corpo e depois o ramo , e consertar esquecido. Pode-se presumir que env deveria ter sido usado em vez do outro objeto . Uma correção para este lugar suspeito pode ser assim:
if (other_tc == other->current_tc) {
tccause = other->CP0_Cause;
} else {
tccause = env->CP0_Cause;
}
Somente os desenvolvedores deste código podem dizer inequivocamente como ele realmente deveria ser. Outro lugar semelhante:
- V523 A instrução 'then' é equivalente à instrução 'else'. translate.c 641
Aviso N3
V547 A expressão '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
....
}
}
Aqui, o analisador descobriu que a condição (comentário 1) nunca seria satisfeita. O valor da variável ret é inicializado pelo resultado da execução da função handle_dependencies , esta função retorna apenas 0 ou -EAGAIN (comentários 3, 4, 5). Um pouco acima, na primeira condição, verificamos o valor ret contra -EAGAIN (comentário 2), então o resultado da expressão ret <0 será sempre falso. Talvez a função handle_dependencies usada para retornar valores diferentes, mas posteriormente, como resultado de, por exemplo, refatoração, o comportamento mudou. Aqui, você só precisa concluir a refatoração. Gatilhos semelhantes:
- A expressão V547 é sempre falsa. qcow2.c 1070
- V547 Expression 's-> state! = MIGRATION_STATUS_COLO' é sempre falso. colo.c 595
- V547 Expression 's-> metadata_entries.present & 0x20' é sempre falso. vhdx.c 769
Aviso N4
V557 A saturação da matriz é possível. A função 'dwc2_glbreg_read' processa o valor '[0..63]'. Inspecione o terceiro argumento. Verifique as linhas: 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
....
}
....
}
Este código tem um problema potencial com saturação de array. Na estrutura DWC2State definida matriz glbreg , consistindo em 28 elementos (comentário 1). Na função dwc2_glbreg_read , nos referimos ao nosso array por índice (comentário 2). Agora, observe que a expressão ( addr - HSOTG_REG (0x000)) >> 2 (comentário 3) é passada como um índice para a função dwc2_glbreg_read , que pode assumir um valor no intervalo [0..63]. Para se convencer disso, preste atenção aos comentários 4 e 5. Talvez, aqui seja necessário ajustar a faixa de valores do comentário 4. Mais gatilhos semelhantes:
- A saturação da matriz V557 é possível. A função 'dwc2_hreg0_read' processa o valor '[0..63]'. Inspecione o terceiro argumento. Verifique as linhas: 814, 1050.hcd-dwc2.c 814
- A saturação da matriz V557 é possível. A função 'dwc2_hreg1_read' processa o valor '[0..191]'. Inspecione o terceiro argumento. Verifique as linhas: 927, 1053.hcd-dwc2.c 927
- A saturação da matriz V557 é possível. A função 'dwc2_pcgreg_read' processa o valor '[0..127]'. Inspecione o terceiro argumento. Verifique as linhas: 1012, 1060.hcd-dwc2.c 1012
Aviso N5
V575 A função 'strerror_s' processa elementos '0'. Inspecione o segundo argumento. comandos-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);
....
}
}
A função strerror_s retorna uma descrição textual do código de erro do sistema. Sua assinatura é parecida com esta:
errno_t strerror_s( char *buf, rsize_t bufsz, errno_t errnum );
O primeiro parâmetro é um ponteiro para o buffer onde a descrição do texto será copiada, o segundo é o tamanho do buffer, o terceiro é o código de erro. No código, 0 é passado como o tamanho do buffer, este é claramente um valor incorreto. Aliás, é possível saber com antecedência quantos bytes devem ser alocados: basta chamar strerrorlen_s , que retorna o comprimento do texto de descrição do erro. Este valor pode ser usado para alocar um buffer de tamanho suficiente.
Aviso N6
V595 O ponteiro 'blen2p' foi utilizado antes de ser verificado em relação a nullptr. Verifique as linhas: 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
....
}
Nesse código, o valor do argumento blen2p é usado primeiro (comentário 1) e, em seguida, verificado para nullptr (comentário 2). Este lugar altamente suspeito parece que você esqueceu de inserir o cheque antes do primeiro uso (comentário 1). Para corrigir, basta adicionar um cheque:
dolog("DirectSound returned misaligned buffer %ld %ld\n",
*blen1p, blen2p ? *blen2p : 0);
Ainda há uma dúvida sobre o argumento blen1p . Provavelmente, também pode ser um ponteiro nulo e aqui você também precisará adicionar uma marca de verificação. Vários outros aspectos positivos semelhantes:
- V595 O ponteiro 'ref' foi utilizado antes de ser verificado em relação a nullptr. Verifique as linhas: 2191, 2193.uri.c 2191
- V595 O ponteiro 'cmdline' foi utilizado antes de ser verificado em relação a nullptr. Verifique as linhas: 420, 425.qemu-io.c 420
- V595 O ponteiro 'dp' foi utilizado antes de ser verificado em relação a nullptr. Verifique as linhas: 288, 294. onenand.c 288
- V595 O ponteiro 'omap_lcd' foi utilizado antes de ser verificado em relação a nullptr. Verifique as linhas: 81, 87. omap_lcdc.c 81
Aviso N7
V597 O compilador pode excluir a chamada de função 'memset', que é usada para liberar o objeto 'op_info'. A função RtlSecureZeroMemory () deve ser usada para apagar os dados privados. 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);
}
}
Neste fragmento de código, a função memset é chamada para o objeto op_info (comentário 1), após o qual o op_info é imediatamente excluído, ou seja, após a limpeza, este objeto não é modificado em nenhum outro lugar. Este é exatamente o caso quando o compilador pode remover a chamada do memset durante o processo de otimização . Para eliminar esse comportamento potencial, você pode usar funções especiais que o compilador nunca remove. Consulte também o artigo " Limpando dados privados com segurança ".
N8
V610 Aviso de comportamento não especificado. Verifique o operador de mudança '>>'. O operando esquerdo é negativo ('número' = [-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;
....
}
Como o número da variável pode ser negativo, o deslocamento para a direita bit a bit é um comportamento não especificado. Para ter certeza de que a variável em questão pode assumir um valor negativo, consulte os comentários 1 e 2. Para eliminar as diferenças no comportamento do seu código em diferentes plataformas, tais casos devem ser evitados.
Mais avisos:
- Comportamento V610 indefinido. Verifique o operador de turno '<<'. O operando esquerdo é negativo ('(hclk_div - 1)' = [-1..15]). aspeed_smc.c 1041
- Comportamento V610 indefinido. Verifique o operador de turno '<<'. O operando esquerdo '(target_long) - 1' é negativo. exec-vary.c 99
- Comportamento V610 indefinido. Verifique o operador de turno '<<'. O operando esquerdo é negativo ('hex2nib (palavras [3] [i * 2 + 2])' = [-1..15]). qtest.c 561
Existem também vários avisos do mesmo tipo, apenas -1 é usado como operando esquerdo . Comportamento
V610 indefinido. Verifique o operador de turno '<<'. O operando esquerdo '-1' é negativo. hppa.c 2702
int print_insn_hppa (bfd_vma memaddr, disassemble_info *info)
{
....
disp = (-1 << 10) | imm10;
....
}
Outros avisos semelhantes:
- Comportamento V610 indefinido. Verifique o operador de turno '<<'. O operando esquerdo '-1' é negativo. hppa.c 2718
- Comportamento V610 indefinido. Verifique o operador de turno '<<'. O operando esquerdo '-0x8000' é negativo. fmopl.c 1022
- Comportamento V610 indefinido. Verifique o operador de turno '<<'. O operando esquerdo '(intptr_t) - 1' é negativo. sve_helper.c 889
Aviso N9
V616 O 'TIMER_NONE' denominado constante com o valor 0 é usado na operação bit a 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
....
}
}
Você pode verificar facilmente se o valor da macro TIMER_NONE é zero (comentário 1). Além disso, esta macro é usada em uma operação bit a bit, cujo resultado sempre será 0. Como resultado, o corpo da instrução condicional se (env-> ttmr & TIMER_NONE) nunca será executado.
Aviso N10
V629 Considere inspecionar a expressão 'n << 9'. Mudança de bit do valor de 32 bits com uma expansão subsequente para o tipo de 64 bits. 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);
....
}
....
}
Neste fragmento de código , uma operação de deslocamento é realizada na variável n , que tem um tipo assinado de 32 bits, então este resultado assinado de 32 bits é expandido para um tipo assinado de 64 bits e, em seguida, como um tipo não assinado, é adicionado ao deslocamento da variável não assinado de 64 bits . Suponha que, no momento em que a expressão é executada, a variável n tenha alguns 9 bits mais significativos. Estamos realizando uma operação de mudança de 9 bits ( BDRV_SECTOR_BITS), e este, por sua vez, é um comportamento indefinido, então, como resultado, podemos obter o bit definido no bit mais significativo. Lembre-se de que esse bit do tipo com sinal é o responsável pelo sinal, ou seja, o resultado pode se tornar negativo. Como n é uma variável com sinal, o sinal será levado em consideração ao expandir. O resultado é então adicionado à variável de deslocamento . A partir dessas considerações, é fácil ver que o resultado da execução da expressão pode ser diferente do pretendido. Uma das soluções possíveis é substituir o tipo da variável n por um tipo sem sinal de 64 bits, ou seja, por uint64_t .
Aqui estão mais alguns gatilhos semelhantes:
- V629 Consider inspecting the '1 << refcount_order' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. qcow2.c 3204
- V629 Consider inspecting the 's->cluster_size << 3' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. qcow2-bitmap.c 283
- V629 Consider inspecting the 'i << s->cluster_bits' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. qcow2-cluster.c 983
- V629 Consider inspecting the expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. vhdx.c 1145
- V629 Consider inspecting the 'delta << 2' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. mips.c 4341
Aviso N11
V634 A prioridade da operação '*' é maior do que a da operação '<<'. É possível que parênteses sejam usados na expressão. nand.c 310
static void nand_command(NANDFlashState *s)
{
....
s->addr &= (1ull << s->addrlen * 8) - 1;
....
}
É apenas um lugar suspeito. Não está claro o que o programador queria fazer no início: deslocamento ou multiplicação. Mesmo que não haja nenhum erro aqui, você ainda precisa olhar para o código novamente e colocar os colchetes corretamente. Este é apenas um daqueles lugares que os desenvolvedores devem olhar para ter certeza de que seu algoritmo está correto. Outros lugares:
- V634 A prioridade da operação '*' é superior à da operação '<<'. É possível que parênteses sejam usados na expressão. exynos4210_mct.c 449
- V634 A prioridade da operação '*' é superior à da operação '<<'. É possível que parênteses sejam usados na expressão. exynos4210_mct.c 1235
- V634 A prioridade da operação '*' é superior à da operação '<<'. É possível que parênteses sejam usados na expressão. exynos4210_mct.c 1264
Aviso N12
V646 Considere inspecionar a lógica do aplicativo. É possível que a palavra-chave 'else' esteja faltando. 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 {
....
}
....
}
....
}
Neste código, a julgar pela formatação, o uso de else if em vez de if sugere- se diretamente . Talvez eles tenham se esquecido de adicionar mais aqui . Então, a opção de correção pode ser assim:
} else if (s->cmd & PL181_CMD_PENDING) { // <= else if
No entanto, existe a possibilidade de que tudo esteja em ordem com este código e a formatação do texto do programa esteja incorreta, o que é confuso. Então, o código pode ser assim:
if (s->cmd & PL181_CMD_INTERRUPT) {
....
}
if (s->cmd & PL181_CMD_PENDING) { // <= if
....
} else {
....
}
Aviso N13
V773 A função foi encerrada sem liberar o ponteiro de 'regra'. Um vazamento de memória é possível. 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
....
}
Neste código, o objeto de regra (comentário 1) é selecionado e adicionado à lista para uso posterior (comentário 3), mas em caso de erro, a função retorna sem excluir o objeto de regra criado anteriormente (comentário 2). Aqui você só precisa tratar corretamente o erro: deletar o objeto criado anteriormente, caso contrário, haverá um vazamento de memória.
Aviso N14
V781 O valor do índice 'ix' é verificado depois de ser usado. Talvez haja um erro na lógica do programa. uri.c 2110
char *uri_resolve_relative(const char *uri, const char *base)
{
....
ix = pos;
if ((ref->path[ix] == '/') && (ix > 0)) {
....
}
Aqui, o analisador detectou uma potencial matriz fora dos limites. Primeiro, o elemento da matriz ref-> path no índice ix é lido e, em seguida, ix é verificado quanto à exatidão ( ix> 0 ). A solução correta aqui é reverter essas ações:
if ((ix > 0) && (ref->path[ix] == '/')) {
Havia vários desses lugares:
- V781 O valor do índice 'ix' é verificado após ser usado. Talvez haja um erro na lógica do programa. uri.c 2112
- V781 O valor do índice 'offset' é verificado após ser usado. Talvez haja um erro na lógica do programa. keymaps.c 125
- V781 O valor da variável 'qualidade' é verificado depois de ser usado. Talvez haja um erro na lógica do programa. Verifique as linhas: 326, 335.vnc-enc-tight.c 326
- V781 O valor do índice 'i' é verificado após ser usado. Talvez haja um erro na lógica do programa. mem_helper.c 1929
Aviso N15
V784 O tamanho da máscara de bits é menor que o tamanho do primeiro operando. Isso causará a perda de bits maiores. 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]);
....
}
Este código executa uma operação bit a bit em objetos de diferentes tipos. O operando esquerdo é o argumento val , que é um tipo sem sinal de 64 bits. O valor recebido do elemento da matriz s-> regs_ro no índice de deslocamento , que tem um tipo sem sinal de 32 bits, é usado como o operando correto . O resultado da operação no lado direito (~ (s-> regs_ro [offset])) é um tipo sem sinal de 32 bits e antes da multiplicação bit a bit se expandirá para o tipo de 64 bits com zeros, ou seja, após calcular a expressão inteira, todos os bits de ordem superior da variável val serão zerados . Esses lugares sempre parecem suspeitos. Aqui, podemos apenas recomendar aos desenvolvedores que revisem este código novamente. Mais semelhantes:
- V784 O tamanho da máscara de bits é menor que o tamanho do primeiro operando. Isso causará a perda de bits maiores. xlnx-zynq-devcfg.c 199
- V784 O tamanho da máscara de bits é menor que o tamanho do primeiro operando. Isso causará a perda de bits maiores. soc_dma.c 214
- V784 O tamanho da máscara de bits é menor que o tamanho do primeiro operando. Isso causará a perda de bits maiores. fpu_helper.c 418
Aviso N16
V1046 Uso não seguro dos tipos 'bool' e 'unsigned int' juntos na operação '& ='. 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);
}
Neste fragmento de código, uma operação AND bit a bit é executada na variável hpd do tipo bool e o resultado da execução da função extract32 , que tem o tipo uint32_t . Como o valor do bit de uma variável booleana só pode ser 0 ou 1, o resultado da expressão sempre será falso se o bit menos significativo retornado pela função extract32 for zero. Vejamos isso com um exemplo. Suponha que hpd seja verdadeiro e a função retorne 2, ou seja, na representação binária, a operação será semelhante a 01 & 10 = 0 e o resultado da expressão será falso . Provavelmente, o programador queria definir o valor como verdadeirose a função retornar algo diferente de zero. Aparentemente, o código precisa ser corrigido para que o resultado da função seja convertido para o tipo bool , por exemplo, assim:
hpd = hpd && (bool)extract32(tcr, 6, 1);
Conclusão
Como você pode ver, o analisador encontrou muitos lugares suspeitos. Talvez os potenciais problemas encontrados ainda não se tenham manifestado de forma alguma, mas a sua presença só pode ser alarmante, pois são capazes de disparar no momento mais inesperado. Visualizar todos os lugares suspeitos com antecedência e corrigir é melhor do que corrigir um fluxo interminável de bugs. Obviamente, para projetos complexos como este, a análise estática pode trazer benefícios tangíveis, especialmente se você organizar uma revisão regular do projeto. Se você quiser experimentar o PVS-Studio para o seu projeto, você pode baixar o analisador e obter uma chave de teste gratuita nesta página.

Se você quiser compartilhar este artigo com um público que fala inglês, use o link de tradução: Evgeniy Ovsannikov. Verificando QEMU usando PVS-Studio .