Validando rdesktop e xrdp usando o analisador PVS-Studio

Image3

Esta é a segunda revisão de uma série de artigos sobre a verificação de programas de código aberto para trabalhar com o protocolo RDP. Nele, consideraremos o cliente rdesktop e o servidor xrdp.

O PVS-Studio foi usado como uma ferramenta para detectar erros. É um analisador de código estático para C, C ++, C # e Java, disponível no Windows, Linux e macOS.

O artigo apresenta apenas os erros que me pareciam interessantes. No entanto, os projetos são pequenos, por isso houve poucos erros :).

Nota O artigo anterior sobre a verificação do projeto FreeRDP pode ser encontrado aqui .

rdesktop


O rdesktop é uma implementação gratuita do cliente RDP para sistemas baseados em UNIX. Também pode ser usado no Windows, se você criar um projeto no Cygwin. Licenciado sob GPLv3.

Esse cliente é muito popular - ele é usado por padrão no ReactOS e você também pode encontrar front-ends gráficos de terceiros. No entanto, ele é bastante velho: o primeiro lançamento ocorreu em 4 de abril de 2001 - na época em que escrevia, tinha 17 anos.

Como observei anteriormente, o projeto é muito pequeno. Ele contém cerca de 30 mil linhas de código, o que é um pouco estranho, dada a sua idade. Para comparação, o FreeRDP contém 320 mil linhas. Aqui está a saída do programa Cloc:

Image1


Código inacessível


V779 Código inacessível detectado. É possível que haja um erro. rdesktop.c 1502

int main(int argc, char *argv[]) { .... return handle_disconnect_reason(deactivated, ext_disc_reason); if (g_redirect_username) xfree(g_redirect_username); xfree(g_username); } 

O erro nos encontra imediatamente na função principal : vemos o código que vem após a declaração de retorno - esse fragmento limpa a memória. No entanto, o erro não representa uma ameaça: toda a memória alocada é limpa pelo sistema operacional após o término do programa.

Falta de tratamento de erros


V557 Array underrun é possível. O valor do índice 'n' pode chegar a -1. rdesktop.c 1872

 RD_BOOL subprocess(char *const argv[], str_handle_lines_t linehandler, void *data) { int n = 1; char output[256]; .... while (n > 0) { n = read(fd[0], output, 255); output[n] = '\0'; // <= str_handle_lines(output, &rest, linehandler, data); } .... } 

O trecho de código nesse caso lê do arquivo para o buffer até que o arquivo termine. No entanto, não há manipulação de erros aqui: se algo der errado, a leitura retornará -1 e a matriz de saída irá além dos limites da matriz.

Usando EOF em char


O V739 EOF não deve ser comparado com um valor do tipo 'char'. O '(c = fgetc (fp))' deve ser do tipo 'int'. ctrl.c 500

 int ctrl_send_command(const char *cmd, const char *arg) { char result[CTRL_RESULT_SIZE], c, *escaped; .... while ((c = fgetc(fp)) != EOF && index < CTRL_RESULT_SIZE && c != '\n') { result[index] = c; index++; } .... } 

Aqui vemos um processamento incorreto para atingir o final do arquivo: se fgetc retornar um caractere cujo código é 0xFF, ele será interpretado como o final do arquivo ( EOF ).

EOF é uma constante, geralmente definida como -1. Por exemplo, na codificação CP1251, a última letra do alfabeto russo tem o código 0xFF, que corresponde ao número -1, se estivermos falando de uma variável do tipo char . Acontece que o caractere 0xFF, como EOF (-1), é percebido como o final do arquivo. Para evitar esses erros, o resultado da função fgetc deve ser armazenado em uma variável int .

Erros de digitação


Fragmento 1

A expressão V547 'write_time' é sempre falsa. disk.c 805

 RD_NTSTATUS disk_set_information(....) { time_t write_time, change_time, access_time, mod_time; .... if (write_time || change_time) mod_time = MIN(write_time, change_time); else mod_time = write_time ? write_time : change_time; // <= .... } 

Talvez o autor deste código tenha confundido || e && fornecidos. Considere as opções possíveis para write_time e change_time :

  • Ambas as variáveis ​​são 0: nesse caso, chegamos ao ramo else : a variável mod_time sempre será 0, independentemente da seguinte condição.
  • Uma das variáveis ​​é 0: mod_time será 0 (desde que a outra variável tenha um valor não negativo), porque o MIN escolherá a menor das duas opções.
  • Ambas as variáveis ​​não são iguais a 0: selecionamos o valor mínimo.

Ao substituir uma condição por write_time && change_time, o comportamento parecerá correto:
  • Uma ou ambas as variáveis ​​não são iguais a 0: selecione um valor diferente de zero.
  • Ambas as variáveis ​​não são iguais a 0: selecionamos o valor mínimo.

Fragmento 2

A expressão V547 é sempre verdadeira. Provavelmente o operador '&&' deve ser usado aqui. disk.c 1419

 static RD_NTSTATUS disk_device_control(RD_NTHANDLE handle, uint32 request, STREAM in, STREAM out) { .... if (((request >> 16) != 20) || ((request >> 16) != 9)) return RD_STATUS_INVALID_PARAMETER; .... } 

Aparentemente, os operadores || e && , == e ! = : a variável não pode aceitar os valores 20 e 9 ao mesmo tempo.

Cópia de linha ilimitada


V512 Uma chamada da função 'sprintf' levará ao estouro do buffer 'caminho completo'. disk.c 1257

 RD_NTSTATUS disk_query_directory(....) { .... char *dirname, fullpath[PATH_MAX]; .... /* Get information for directory entry */ sprintf(fullpath, "%s/%s", dirname, pdirent->d_name); .... } 

Ao considerar a função, ficará completamente claro que esse código não causa problemas. No entanto, eles podem surgir no futuro: uma mudança descuidada e obtemos um estouro de buffer - o sprintf não é limitado por nada; portanto, ao concatenar caminhos, podemos ir além dos limites da matriz. É recomendável observar esta chamada no snprintf (caminho completo, PATH_MAX, ....) .

Condição redundante


V560 Uma parte da expressão condicional é sempre verdadeira: add> 0. scard.c 507

 static void inRepos(STREAM in, unsigned int read) { SERVER_DWORD add = 4 - read % 4; if (add < 4 && add > 0) { .... } } 

Marcar add> 0 é inútil: a variável sempre será maior que zero, porque a leitura% 4 retornará o restante da divisão e nunca será 4.

xrdp


xrdp é uma implementação de servidor RDP de código aberto. O projeto está dividido em 2 partes:

  • xrdp - implementação de protocolo. Distribuído sob a licença Apache 2.0.
  • xorgxrdp - Um conjunto de drivers Xorg para uso com o xrdp. Licença - X11 (como MIT, mas proíbe o uso em publicidade)

O desenvolvimento do projeto é baseado nos resultados do rdesktop e do FreeRDP. Inicialmente, tive que usar um servidor VNC separado para trabalhar com gráficos, ou um servidor X11 especial com suporte a RDP - X11rdp, mas com o advento do xorgxrdp, eles não eram mais necessários.

Neste artigo, não tocaremos no xorgxrdp.

O projeto xrdp, como o anterior, é muito pequeno e contém cerca de 80 mil linhas.

Image2


Mais erros de digitação


V525 O código contém a coleção de blocos semelhantes. Verifique os itens 'r', 'g', 'r' nas linhas 87, 88, 89. rfxencode_rgb_to_yuv.c 87

 static int rfx_encode_format_rgb(const char *rgb_data, int width, int height, int stride_bytes, int pixel_format, uint8 *r_buf, uint8 *g_buf, uint8 *b_buf) { .... switch (pixel_format) { case RFX_FORMAT_BGRA: .... while (x < 64) { *lr_buf++ = r; *lg_buf++ = g; *lb_buf++ = r; // <= x++; } .... } .... } 

Esse código foi retirado da biblioteca librfxcodec que implementa o codec jpeg2000 para que o RemoteFX funcione. Aqui, aparentemente, os canais de dados gráficos são misturados - em vez da cor "azul", "vermelho" é gravado. Esse erro provavelmente apareceu como resultado de copiar e colar.

O mesmo problema ocorreu em uma função semelhante rfx_encode_format_argb , que o analisador também nos falou sobre:

V525 O código contém a coleção de blocos semelhantes. Verifique os itens 'a', 'r', 'g', 'r' nas linhas 260, 261, 262, 263. rfxencode_rgb_to_yuv.c 260

 while (x < 64) { *la_buf++ = a; *lr_buf++ = r; *lg_buf++ = g; *lb_buf++ = r; x++; } 

Declaração de matriz


A saturação da matriz V557 é possível. O valor do índice 'i - 8' pode chegar a 129. genkeymap.c 142

 // evdev-map.c int xfree86_to_evdev[137-8+1] = { .... }; // genkeymap.c extern int xfree86_to_evdev[137-8]; int main(int argc, char **argv) { .... for (i = 8; i <= 137; i++) /* Keycodes */ { if (is_evdev) e.keycode = xfree86_to_evdev[i-8]; .... } .... } 

A declaração e a definição da matriz nesses dois arquivos são incompatíveis - o tamanho difere de 1. No entanto, nenhum erro ocorre - o tamanho correto é indicado no arquivo evdev-map.c, portanto, não há como escapar dos limites. Portanto, essa é apenas uma falha fácil de corrigir.

Comparação inválida


V560 Uma parte da expressão condicional é sempre falsa: (cap_len <0). xrdp_caps.c 616

 // common/parse.h #if defined(B_ENDIAN) || defined(NEED_ALIGN) #define in_uint16_le(s, v) do \ .... #else #define in_uint16_le(s, v) do \ { \ (v) = *((unsigned short*)((s)->p)); \ (s)->p += 2; \ } while (0) #endif int xrdp_caps_process_confirm_active(struct xrdp_rdp *self, struct stream *s) { int cap_len; .... in_uint16_le(s, cap_len); .... if ((cap_len < 0) || (cap_len > 1024 * 1024)) { .... } .... } 

Em uma função, uma variável do tipo sem assinatura curta é lida em uma variável do tipo int . A verificação não é necessária aqui, porque lemos uma variável de um tipo não assinado e atribuímos o resultado a uma variável maior, para que a variável não possa assumir um valor negativo.

Verificações desnecessárias


V560 Uma parte da expressão condicional é sempre verdadeira: (bpp! = 16). libxrdp.c 704

 int EXPORT_CC libxrdp_send_pointer(struct xrdp_session *session, int cache_idx, char *data, char *mask, int x, int y, int bpp) { .... if ((bpp == 15) && (bpp != 16) && (bpp != 24) && (bpp != 32)) { g_writeln("libxrdp_send_pointer: error"); return 1; } .... } 

Verificações de desigualdade não fazem sentido aqui, porque já temos uma comparação no início. É provável que este seja um erro de digitação e o desenvolvedor desejou usar o || para filtrar argumentos inválidos.

Conclusão


Durante a inspeção, nenhum erro sério foi detectado, mas muitas deficiências foram encontradas. No entanto, esses projetos são usados ​​em muitos sistemas, embora de escopo pequeno. Um projeto pequeno não precisa ter muitos erros; portanto, você não deve julgar o trabalho do analisador apenas em projetos pequenos. Você pode ler mais sobre isso no artigo " Sensações, confirmadas por números ".

Você pode baixar uma versão de avaliação do PVS-Studio em nosso site .



Se você deseja compartilhar este artigo com um público que fala inglês, use o link para a tradução: Sergey Larin. Verificando o rdesktop e o xrdp com o PVS-Studio

Source: https://habr.com/ru/post/pt447880/


All Articles