不知道缓冲区大小时如何避免危险的vsprintf

问题描述 投票:2回答:5
__inline int my_sprintf (char *dest,char *format,...)
{
    va_list va;
    va_start(va,format);
    return vsprintf(dest,format,va);
}

我的问题是我无法将缓冲区大小参数添加到my_sprintf,因为它已在超过50k的地方使用,因此我无法将vsprintf替换为vsprintf_s或vsnprintf。

使上述功能更安全的替代方法?

c static-analysis stdio cstdio
5个回答
5
投票

您在这里问的是(一个)著名问题的专业化:“ 如果我只有一个指针,如何获得数组的大小?

无法找出dest指向的对象的大小。您最好的选择可能是硬着头皮,更改那50k的位置以通过大小。


您可能没有告诉我们更多的代码。例如,在您提到的那些“ 50k”地方,尺寸是已知的吗?如果是这样,您可以摆脱一个肮脏的可变参数宏,该宏在幕后使用sizeof,然后调用一个带有length参数的函数。


3
投票

[抱歉,这里没有银弹,就像@cnicutar已经提到的。

您可以先限制缓冲区大小并在发生溢出时断言。类似于:

#define SPRINTF_TRACE_BUFSIZE 4096

int my_sprintf( char* dest, const char* fmt, ... )
{
    /* in threaded code use malloc(3) instead */
    static char trace_buf[SPRINTF_TRACE_BUFSIZE];

    va_list va;
    va_start( va, fmt );
    int rc = vsnprintf( trace_buf, SPRINTF_TRACE_BUFSIZE, fmt, va );

    assert( rc != -1 && rc < SPRINTF_TRACE_BUFSIZE );

    memcpy( dest, trace_buf, rc + 1 ); /* +1 for \0 terminator */
    return rc;
}

然后开始降低跟踪缓冲区的大小,直到断言开始触发为止。此时,您可以找到并修复有问题的电话。

这当然会减慢整个系统的速度,但是我们这里不讨论性能。

只需强调一下-这是一个与大型旧现有代码库打交道的快速而肮脏的技巧,请勿将其用于新开发


1
投票

OP注释“动态分配了大量缓冲区,...”。 malloc()realloc()calloc()free()等可以用存储大小的包装函数重写。

typedef union {
  max_align_t align;
  size_t sz;
} my_header;

void* my_malloc(size_t size) {
  my_header *p = malloc(sizeof *p + size);
  if (p) {
    p->sz = size;
    p++;
  }
  return p;
}

size_t my_size(const void *p) {
  if (p) {
    const my_header *head = p;
    return head[-1].sz;
  }
  return 0;
}

void my_free(void *p) {
  if (p) {
    my_header *head = p;
    free(--head);
  }
}

全部other * .c文件使用*调用某些* .h文件

#define malloc my_malloc
#define free my_free
void *my_malloc(size_t size);
void my_free(void *p);
size_t my_size(const void *p);

现在使用分配的指针]调用my_sprintf()时...

int my_sprintf (char *dest,char *format,...) {
  va_list va;
  va_start(va,format);
  size_t n = my_size(dest);
  return vsnprintf(dest,n,format,va);
}

此外,还可以在前面加上幻数

以帮助识别所传递的指针是否确实是my_allcoated()

包装分配函数也是一种确定各种分配问题的方法:双倍释放,最大使用率,所有指针均已释放,...


[[编辑]在5年后。

代码需要确保对齐-重新编写代码。


对于C11之前的版本,请使用宽类型的并集来代替max_align_t

typedef union {
  double d;
  long l;
  void *p;
  void (*fp)();
  // With C99
  complex long double cld;
  long long ll;

  size_t sz;
} my_header; 

0
投票

这种大规模的运动重构工作乞求自动化。更改my_sprintf函数本身的行为是微不足道的,因此我将把练习留给读者。如您所述,呼叫是最困难的部分。我假设这些调用的结构如下:


0
投票

[如果在传递my_sprintf()而不是真实的char *的情况下,您可以使用宏来提供帮助。

© www.soinside.com 2019 - 2024. All rights reserved.