2

我有一些用 C 语言编写的软件,在我测试过的每个系统上都运行良好,直到现在。我们的一位系统管理员已将它安装在我们的集群上,并且出现了一些奇怪的行为。

在程序中,我正在使用京都内阁创建一个 DBM 数据库,尽管在此框中,该过程在创建数据库之前失败。该程序采用命令行参数来指定内存映射的大小和 KC 库使用的存储桶数。它根据kcdbopen. 即,如果用户传递--mmap-size=1024 --num-buckets=256了一个名为 的 db 文件,则构造foo.kch一个字符串并将其传递给.foo.kch#msiz=1024#bnum=256kcdbopen

正如我所说,这通常可以正常工作。但是,在这台特定的机器上,该进程将失败,并出现无法分配内存的错误。另外,一些调试文本显示它试图创建的文件以一些垃圾字符开头,并且 msiz 和 bnum 数字已经溢出,变得很大:

Failed to open database �!�u�foo.kch#msiz=249821240256#bnum=4199616: Cannot allocate memory

argp_parse用来解析命令行参数。该代码的相关部分是:

struct arguments
{
  char *args[1];
  unsigned long int mmap_size;
  unsigned long int num_buckets;
};

static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
  struct arguments *arguments = state->input;

  switch (key)
    {
    case 'm':
      arguments->mmap_size = arg ? atol (arg) : 1024;
      break;

    case 'b':
      arguments->num_buckets = arg ? atol (arg) : 100;
      break;

    case ARGP_KEY_ARG:
      if (state->arg_num >= 1)
        argp_usage (state);
      arguments->args[state->arg_num] = arg;
      break;
...<snip>....

我试图在构建文件名字符串时格外(过度?)谨慎:

void *
zdb_create (char *dbfile, unsigned long int mmap_size, unsigned long int num_buckets,
            bool verbose)
{
...<snip>...
  char mmap_str[32];
  char buckets_str[32];
  char db_str[512];
  if (strlen (dbfile) > sizeof (db_str) - sizeof (buckets_str) - sizeof (mmap_str))
    error (EXIT_FAILURE, errno, "Filename too long");
  strncat (db_str, dbfile, strlen (dbfile));
  snprintf (mmap_str, sizeof(mmap_str), "#msiz=%lu", mmap_size);
  if (strlen (dbfile) + strlen (mmap_str) < sizeof (db_str))
    strncat (db_str, mmap_str, 32);
  else
    error (EXIT_FAILURE, errno, "Filename too long");
  snprintf (buckets_str, sizeof(buckets_str), "#bnum=%lu", num_buckets);
  if (strlen (dbfile) + strlen (mmap_str) < sizeof (db_str))
    strncat (db_str, buckets_str, 32);
  else
    error (EXIT_FAILURE, errno, "Filename too long");
  db = zdb_open (db_str, ZDB_CREATOR, false);
...<snip>...

所以,我刚刚了解到有一个strtoul函数,这可能是问题的根源;atol仅转换为标准long而不是unsigned long. 但是,我想 100% 确定我在去之前解决了问题并再次打扰系统管理员尝试重新安装(不幸的是,我不可能自己测试它,AFAICT)。这种溢出行为最可能的来源是什么?整数溢出和字符串开头的垃圾字符是否相关?为什么这适用于除此之外的大多数系统?

info该系统使用的是 Linux 2.6.18、gcc 4.1.2,而且,根据它的页面很难判断 libc 的哪个版本...版本 2.3.x。我只在较新的系统上进行了测试,例如 Linux 2.6.32 / gcc 4.4.3 / libc 2.8 和 Linux 3.9.8 / gcc 4.8.1 / libc 2.17。

谢谢!

4

1 回答 1

2

这是有问题的:

  char db_str[512]; // not initialized!
  if (strlen (dbfile) > sizeof (db_str) - sizeof (buckets_str) - sizeof (mmap_str))
    error (EXIT_FAILURE, errno, "Filename too long");
  strncat (db_str, dbfile, strlen (dbfile)); // adding to an uninitialized string!

你想像这样初始化 db_str:

  char db_str[512] = "";
于 2013-07-04T16:41:30.943 回答