PVS-Studio アナラむザヌを䜿甚した rdesktop ず xrdp の確認

PVS-Studio アナラむザヌを䜿甚しお rdesktop ず xrdp を確認する
これは、RDP プロトコルで動䜜するオヌプン ゜ヌス プログラムのテストに関する䞀連の蚘事の XNUMX 番目のレビュヌです。 その䞭で、rdesktop クラむアントず xrdp サヌバヌを芋おいきたす。

゚ラヌを特定するツヌルずしお䜿甚される PVSスタゞオ。 これは、C、C++、C#、および Java 蚀語甚の静的コヌド アナラむザヌであり、Windows、Linux、および macOS プラットフォヌムで利甚できたす。

この蚘事では、私にずっお興味深いず思われた゚ラヌのみを玹介したす。 ただし、プロゞェクトは小さいため、間違いはほずんどありたせんでした:)。

泚意。 FreeRDP プロゞェクトの怜蚌に関する以前の蚘事はこちらをご芧ください。 ここで.

rdesktop

rdesktop — UNIX ベヌスのシステム甚の RDP クラむアントの無料実装。 Cygwin でプロゞェクトをビルドすれば、Windows でも䜿甚できたす。 GPLv3 に基づいおラむセンスされおいたす。

このクラむアントは非垞に人気があり、ReactOS ではデフォルトで䜿甚されおおり、サヌドパヌティのグラフィカル フロント゚ンドも芋぀けるこずができたす。 しかし、圌はかなり高霢です。最初のリリヌスは 4 幎 2001 月 17 日に行われ、この蚘事の執筆時点で圌は XNUMX 歳です。

先ほども述べたように、このプロゞェクトは非垞に小芏暡です。 これには玄 30 行のコヌドが含たれおおり、叀いものを考えるず少し奇劙です。 比范のために、FreeRDP には 320 䞇行が含たれおいたす。 Cloc プログラムの出力は次のずおりです。

PVS-Studio アナラむザヌを䜿甚しお rdesktop ず xrdp を確認する

到達䞍胜なコヌド

V 䜿甚できないコヌドが怜出されたした。 ゚ラヌが存圚する可胜性がありたす。 rデスクトップ.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);
}

゚ラヌは関数内ですぐに発生したす メむン: 挔算子の埌にコヌドが来るのがわかりたす。 return — このフラグメントはメモリ クリヌニングを実行したす。 ただし、この゚ラヌは脅嚁ではありたせん。割り圓おられたメモリはすべお、プログラムの終了埌にオペレヌティング システムによっおクリアされたす。

゚ラヌ凊理なし

V 配列アンダヌランの可胜性がありたす。 「n」むンデックスの倀は -1 に達する可胜性がありたす。 rデスクトップ.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] = ' '; // <=
    str_handle_lines(output, &rest, linehandler, data);
  }
  ....
}

この堎合のコヌド スニペットは、ファむルが終了するたでファむルからバッファヌに読み取りたす。 ただし、ここでぱラヌ凊理は行われたせん。䜕か問題が発生した堎合は、 read -1 が返され、配列はオヌバヌランしたす 出力.

char型でのEOFの䜿甚

V EOF は「char」タむプの倀ず比范しないでください。 '(c = fgetc(fp))' は '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++;
  }
  ....
}

ここでは、ファむルの末尟に到達したずきの凊理が間違っおいるこずがわかりたす。 fgetc コヌドが 0xFF の文字を返すず、ファむルの終わりずしお解釈されたす (EOF).

EOF これは定数であり、通垞は -1 ずしお定矩されたす。 たずえば、CP1251 ゚ンコヌディングでは、ロシア語のアルファベットの最埌の文字はコヌド 0xFF であり、次のような倉数に぀いお話しおいる堎合、これは数倀 -1 に察応したす。 チャリオット。 シンボル0xFFは次のようになりたす。 EOF (-1) はファむルの終わりずしお解釈されたす。 このような゚ラヌを避けるために、関数の結果は次のようになりたす。 fgetc 次のような倉数に栌玍する必芁がありたす int型.

タむプミス

フラグメント 1

V 匏「write_time」は垞に false です。 ディスク.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; // <=
  ....
}

おそらくこのコヌドの䜜成者は間違えたのではないでしょうか || О && 状態で。 可胜な倀のオプションを怜蚎しおみたしょう 曞き蟌み時間 О 倉曎時間:

  • 䞡方の倉数が 0 に等しい: この堎合、最終的に分岐になりたす。 ほかに 倉数 mod_time その埌の条件に関係なく、垞に 0 になりたす。
  • 倉数の 0 ぀は XNUMX です。 mod_time は 0 になりたす (他の倉数の倀が負でない堎合)。 MIN XNUMX ぀のオプションのうち小さい方を遞択したす。
  • 䞡方の倉数が 0 に等しくありたせん。最小倀を遞択しおください。

条件を次のように眮き換えるず、 曞き蟌み時間 && 倉曎時間 動䜜は正しく芋えるでしょう:

  • 0 ぀たたは䞡方の倉数が XNUMX に等しくありたせん。れロ以倖の倀を遞択しおください。
  • 䞡方の倉数が 0 に等しくありたせん。最小倀を遞択しおください。

フラグメント 2

V 匏は垞に true です。 おそらくここでは「&&」挔算子を䜿甚する必芁がありたす。 ディスク.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;
  ....
}

どうやらここでもオペレヌタヌが混同されおいるようです || О &&たたは == О !=: 倉数は倀 20 ず 9 を同時に持぀こずはできたせん。

無制限の行コピヌ

V 「sprintf」関数を呌び出すず、バッファ「fullpath」のオヌバヌフロヌが発生したす。 ディスク.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);
  ....
}

関数党䜓を芋るず、このコヌドが問題を匕き起こさないこずが明らかになりたす。 ただし、将来的には発生する可胜性がありたす。XNUMX ぀の䞍泚意な倉曎でバッファ オヌバヌフロヌが発生する可胜性がありたす。 スプリント は䜕にも制限されないため、パスを連結するずきに配列の境界を越えるこずができたす。 この呌び出しに泚目するこずをお勧めしたす snprintf(フルパス, PATH_MAX, 
.).

冗長条件

V 条件匏の䞀郚は垞に true です: add > 0。shard.c 507

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

ПрПверка > 0 を远加 ここではその必芁はありたせん。倉数は垞にれロより倧きくなりたす。 %4 を読み取りたす 陀算の䜙りを返したすが、4 に等しくなるこずはありたせん。

xrdp

xrdp — オヌプン゜ヌス コヌドを䜿甚した RDP サヌバヌの実装。 プロゞェクトは 2 ぀の郚分に分かれおいたす。

  • xrdp - プロトコルの実装。 Apache 2.0 ラむセンスに基づいお配垃されたす。
  • xorgxrdp - xrdp で䜿甚する Xorg ドラむバヌのセット。 ラむセンス - X11 (MIT ず同様ですが、広告での䜿甚を犁止したす)

プロゞェクトの開発は、rdesktop ず FreeRDP の結果に基づいおいたす。 圓初、グラフィックスを操䜜するには、別の VNC サヌバヌ、たたは RDP サポヌトを備えた特別な X11 サヌバヌ (X11rdp) を䜿甚する必芁がありたしたが、xorgxrdp の登堎により、その必芁はなくなりたした。

この蚘事では、xorgxrdp に぀いおは扱いたせん。

xrdp プロゞェクトは、前のプロゞェクトず同様に非垞に小さく、玄 80 行が含たれおいたす。

PVS-Studio アナラむザヌを䜿甚しお rdesktop ず xrdp を確認する

タむプミスが増える

V コヌドには、類䌌したブロックのコレクションが含たれおいたす。 87、88、89 行目の項目「r」、「g」、「r」を確認しおください。 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++;
      }
      ....
  }
  ....
}

このコヌドは、RemoteFX の jpeg2000 コヌデックを実装する librfxcodec ラむブラリから取埗されたものです。 ここでは、明らかにグラフィック デヌタ チャネルが混同されおおり、「青」色の代わりに「赀」が蚘録されおいたす。 この゚ラヌは、コピヌペヌストの結果ずしお発生した可胜性が高くなりたす。

同様の機胜で同じ問題が発生したした rfx_encode_format_argb、アナラむザヌは次のようにも教えおくれたした。

V コヌドには、類䌌したブロックのコレクションが含たれおいたす。 260、261、262、263 行目の項目「a」、「r」、「g」、「r」を確認しおください。 rfxencode_rgb_to_yuv.c 260

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

配列宣蚀

V 配列オヌバヌランの可胜性がありたす。 「i — 8」むンデックスの倀は 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];
    ....
  }
  ....
}

これら 1 ぀のファむルの配列の宣蚀ず定矩には互換性がありたせん。サむズが XNUMX 異なりたす。ただし、゚ラヌは発生したせん。evdev-map.c ファむルには正しいサむズが指定されおいるため、範囲倖は発生したせん。 したがっお、これは簡単に修正できる単なるバグです。

誀った比范

V 条件匏の䞀郚は垞に false: (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))
  {
    ....
  }
  ....
}

関数は型倉数を読み取りたす 未眲名のショヌト のような倉数に int型。 笊号なし倉数を読み取り、その結果をより倧きな倉数に代入しおいるため、倉数は負の倀を取るこずができないため、ここではチェックは必芁ありたせん。

䞍芁なチェック

V 条件匏の䞀郚は垞に true になりたす: (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;
  }
  ....
}

最初に比范がすでに行われおいるため、ここでは䞍等匏チェックは意味がありたせん。 おそらくこれはタむプミスであり、開発者は挔算子を䜿甚したかったのでしょう。 || 無効な匕数を陀倖したす。

たずめ

監査では重倧な゚ラヌは発芋されたせんでしたが、倚くの欠点が芋぀かりたした。 ただし、これらの蚭蚈は、範囲は狭いずはいえ、倚くのシステムで䜿甚されおいたす。 小芏暡なプロゞェクトには必ずしも倚くの゚ラヌが含たれるわけではないため、小芏暡なプロゞェクトだけでアナラむザヌのパフォヌマンスを刀断すべきではありたせん。 これに぀いおは、蚘事「」で詳しく読むこずができたす。数字で確かめた想い"

PVS-Studio の詊甚版を匊瀟からダりンロヌドできたす。 オンラむン.

PVS-Studio アナラむザヌを䜿甚しお rdesktop ず xrdp を確認する

この蚘事を英語圏の読者ず共有したい堎合は、翻蚳リンクを䜿甚しおください: Sergey Larin。 PVS-Studio で rdesktop ず xrdp を確認する

出所 habr.com

コメントを远加したす