【问题标题】:Is this snprintf method a safe way to copy strings?这个 snprintf 方法是复制字符串的安全方法吗?
【发布时间】:2022-01-17 05:13:18
【问题描述】:

我需要你的建议。我使用这种方式来复制知道最大大小的字符串以不超过但其中一些字符串不以空终止符结尾。这只是一个sn-p。

void my_strcpy(char* dest, const char* src, const size_t max_size)
{
    snprintf(dest, max_size, "%.*s", max_size - 1, src);
}

有没有更安全的方法? strlcpy? (我这里没有处理错误。)

【问题讨论】:

  • 不安全:* 需要 int。试试snprintf(dest, max_size, "%.*s", (int)(max_size - 1), src);
  • 感谢您的回复
  • strlcpy() 是一个非标准函数。 strncpy() 不如 snprintf() 安全,因为它不能保证以 nul 终止字符串。您将 实际 缓冲区大小传递给 snprintf()
  • "但其中一些字符串不以空终止符结尾。"在 C 术语中是矛盾的。 C 库将 string 定义为包含 null 字符。也许更好的是“但其中一些字符数组......”
  • 如果它们不以 nul 结尾,则不能将它们传递给字符串处理函数。有长度限制的是保护destination缓冲区,而不是限制源。为此使用memcpy

标签: c string char overflow strcpy


【解决方案1】:

由于多种原因,您的方法并不完全安全:

  • snprintf %.*s 说明符的精度参数必须转换为 int。如果size_t 在您的目标系统上具有不同的大小,则行为未定义。你应该使用演员(int)(max_size - 1)
  • 如果 max_size > INT_MAX 即使有演员表,你的方法也会失败。
  • 如果max_size == 0snprintf() 可能仍会从src 读取字节,这可能会导致未定义的行为,尤其是在源字符串不是以空值结尾的情况下。
  • 如果源字符串没有根据当前选择的语言环境正确编码,snprintf 可能会停止复制并返回 -1,从而使目标不带空终止符。

不清楚您要达到的目标:

我使用这种方式复制知道最大大小的字符串以不超过但其中一些字符串不以空终止符结尾

max_size 是像 strncat 那样复制的最大字节数,还是像 snprintf 那样是包含空终止符空间的目标数组的长度?

哪些字符串没有空终止符?源字符串还是目标数组的结果内容?

按照编码,参数max_size 是目标数组的长度,包括空终止符。

为避免上述问题,这里有一些独立的替代方案:

一个非常好用的截断版本

char array[SIZE];
my_strcpy_trunc(array, sizeof array, source);
// copy source string to an array of length size
// truncate contents to fit in the destination array
// return 0 if successful and no truncation occurred
// return 1 if truncation occurred
// return 2 if src is NULL, destination set to an empty string
// return -1 if arguments are invalid, no copy occurred
int my_strcpy_trunc(char *dest, size_t size, const char *src) {
    if (dest && size) {
        if (src) {
            for (;;) {
                if ((*dest++ = *src++) == '\0')
                    return 0;  // success
                if (--size == 0) {
                    dest[-1] = '\0';
                    return 1;  // truncation occurred
                }
            }
        }
        *dest = '\0';
        return 2;  // src is null pointer
    } else {
        return -1; // invalid dest
    }
}

类似于strncat 的限制版本,您假设目的地至少有n+1 字节可用,恕我直言,这是一种不太安全的方法:

// copy source string up to a maximum of n bytes and set the null terminator to an array of length size
// return 0 if successful
// return 1 if successful and src length larger than n
// return 2 if src is NULL, destination set to an empty string
// return -1 if arguments are invalid, no copy occurred
int my_strcpy_limit(char *dest, const char *src, size_t n) {
    if (dest) {
        if (src) {
            while (n --> 0) {
                if ((*dest++ = *src++) == '\0')
                    return 0;  // success
            }
            *dest = '\0';
            return *src ? 1 : 0;
        }
        *dest = '\0';
        return 2;  // src is null pointer
    } else {
        return -1; // invalid dest
    }
}

【讨论】:

    【解决方案2】:

    OP 的使用看起来不错 - 可能有点迂腐,有大小限制和精度

    "%.*s" 允许指针指向一个没有空字符字符数组。复制将限于精度或 空字符,以先到者为准。

    结果 dest[] 绝不应该以有效参数结尾的非空字符。

    我会添加一些测试:

    // return error code
    int my_strcpy(char* dest, const char* src, size_t max_size) {
      assert(dest && src && max_size && max_size < INT_MAX); // Validate arguments.
      //                                         v-- use int here --v
      int len = snprintf(dest, max_size, "%.*s", (int) (max_size - 1), src);
      return (len < 0 || (unsigned) len >= max_size);
    }
    

    我怀疑sprintf(dest, "%.*s", max_size - 1, src); 也足够了,预计它不会检测到过长的src


    当然可以使用类似的东西

    void my_strcpy(char* restrict dest, const char* restrict src, size_t size) {
      if (dest && src && size) {
        while (--size > 0 && *src) {
          *dest++ = *src++;
        }
        *dest = 0;
      }
    }
    

    restrict 这里暗示src/dest 不重叠。

    【讨论】:

      猜你喜欢
      • 2014-06-08
      • 2016-07-16
      • 2019-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-10
      • 2018-03-02
      相关资源
      最近更新 更多