【问题标题】:Segmentation fault using snprintf使用 snprintf 的分段错误
【发布时间】:2013-10-27 20:28:47
【问题描述】:

我在使用 snprintf 时遇到了一个奇怪的分段错误。

这是我的代码:

size_t tmp_size = win_data_width; // win_data_width is the width of a Ncurses window

char *tmp = new(tmp_size);   // <------- new == malloc MACRO
snprintf(tmp,
    tmp_size,
    "%-15.20s"       /* Process name */
    "%10u"           /* PID          */
    " %-15.10s"      /* Operation    */
    "%-50.45s"       /* Path         */
    "%-10.10s"       /* Result       */
    " %-200.200s\n", /* Details      */

    i->pname,
    i->pid,
    i->operation,
    i->path,
    i->result,
    i->details
);

这是 GDB bt 的完整输出:

#0  __strnlen_sse2 () at ../sysdeps/x86_64/multiarch/../strnlen.S:34
No locals.
#1  0x00007ffff7400e09 in _IO_vfprintf_internal (s=s@entry=0x7fffffffdb80, format=<optimized out>,
    format@entry=0x402f38 "%-15.20s%10u %-15.10s%-50.45s%-10.10s %-200.200s\n", ap=ap@entry=0x7fffffffdce8) at vfprintf.c:1655
        len = <optimized out>
        string_malloced = <optimized out>
        step0_jumps = {0, -11263, -4214, -4124, -4027, -3937, -3826, -3532, -3116, -2815, -2692, -1937, -2239, -2139, -1766, -16687, 265, 280, 47,
          2130, -15904, -28, 883, -5283, -5210, -17380, 567, -2039, -2139, -3638}
        space = 0
        is_short = 0
        use_outdigits = 0
        step1_jumps = {0, 0, 0, 0, 0, 0, 0, 0, 0, -2815, -2692, -1937, -2239, -2139, -1766, -16687, 265, 280, 47, 2130, -15904, -28, 883, -5283,
          -5210, -17380, 567, -2039, -2139, 0}
        group = 0
        prec = 10
        step2_jumps = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2692, -1937, -2239, -2139, -1766, -16687, 265, 280, 47, 2130, -15904, -28, 883, -5283, -5210,
          -17380, 567, -2039, -2139, 0}
        string = <optimized out>
        left = 1
        is_long_double = 0
        width = 10
        step3a_jumps = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2037, 0, 0, 0, -1766, -16687, 265, 280, 47, 0, 0, 0, 0, -5283, 0, 0, 0, 0, 0, 0}
        alt = 0
        showsign = 0
        is_long = 0
        is_char = 0
        pad = 32 ' '
        step3b_jumps = {0 <repeats 11 times>, -2239, 0, 0, -1766, -16687, 265, 280, 47, 2130, -15904, -28, 883, -5283, -5210, -17380, 567, 0, 0, 0}
        step4_jumps = {0 <repeats 14 times>, -1766, -16687, 265, 280, 47, 2130, -15904, -28, 883, -5283, -5210, -17380, 567, 0, 0, 0}
        is_negative = <optimized out>
        number = <optimized out>
        base = <optimized out>
        the_arg = {pa_wchar = 0 L'\000', pa_int = 0, pa_long_int = 0, pa_long_long_int = 0, pa_u_int = 0, pa_u_long_int = 0, pa_u_long_long_int = 0,
        pa_double = 0, pa_long_double = 0, pa_string = 0x0, pa_wstring = 0x0, pa_pointer = 0x0, pa_user = 0x0}
        spec = 115 's'
        _buffer = {__routine = 0x1, __arg = 0x7ffff779ac94, __canceltype = 6313844, __prev = 0x7ffff74c3a84 <___vsprintf_chk+148>}
        _avail = <optimized out>
        thousands_sep = 0x0
        grouping = 0xffffffffffffffff <Address 0xffffffffffffffff out of bounds>
        done = 91
        f = 0x402f5c "s %-200.200s\n"
        lead_str_end = 0x402f38 "%-15.20s%10u %-15.10s%-50.45s%-10.10s %-200.200s\n"
        end_of_spec = <optimized out>
        work_buffer = "\001\200\255\373\000\000\000\000tW`\000\000\000\000\000tW`\000\000\000\000\000tW`\000\000\000\000\000tW`\000\000\000\000\000wW`\000\000\000\000\000\377\377\377\377\377\377\377\377tW`\000\000\000\000\000\377\377\377\377\377\377\377\377", '\000' <repeats 40 times>, "d\000\000\000\004\000\000\000QX`", '\000' <repeats 21 times>, "\020\000\000\000\000\000\000\000CX`\000\000\000\000\000\000\000\000\000[C\033[\000\000\000\000\000\000\000\000\000\330\377\377\377\177\000\000\000\330\377\377\377\177\000\000\377\377\377\377\000\000\000\000"...
        workstart = 0x0
        workend = 0x7fffffffdb38 "\200\333\377\377\377\177"
        ap_save = {{gp_offset = 24, fp_offset = 48, overflow_arg_area = 0x7fffffffddc0, reg_save_area = 0x7fffffffdd00}}
        nspecs_done = 4
        save_errno = 0
        readonly_format = 0
        args_malloced = 0x0
        specs = 0x7fffffffd670
        specs_malloced = false
        jump_table = "\001\000\000\004\000\016\000\006\000\000\a\002\000\003\t\000\005\b\b\b\b\b\b\b\b\b\000\000\000\000\000\000\000\032\000\031\000\023\023\023\000\035\000\000\f\000\000\000\000\000\000\025\000\000\000\000\022\000\r\000\000\000\000\000\000\032\000\024\017\023\023\023\n\017\034\000\v\030\027\021\026\f\000\025\033\020\000\000\022\000\r"
        __PRETTY_FUNCTION__ = "_IO_vfprintf_internal"
#2  0x00007ffff7427655 in _IO_vsnprintf (
    string=0x646b90 "0Bd", ' ' <repeats 20 times>, "49  Nd", ' ' <repeats 62 times>, "Ok         Read 8 bytes (was requested to read 16)",
    maxlen=<optimized out>, format=0x402f38 "%-15.20s%10u %-15.10s%-50.45s%-10.10s %-200.200s\n", args=args@entry=0x7fffffffdce8) at vsnprintf.c:119
        sf = {f = {_sbf = {_f = {_flags = -72515583,
                _IO_read_ptr = 0x646b90 "0Bd", ' ' <repeats 20 times>, "49  Nd", ' ' <repeats 62 times>, "Ok         Read 8 bytes (was requested to read 16)",
                _IO_read_base = 0x646b90 "0Bd", ' ' <repeats 20 times>, "49  Nd", ' ' <repeats 62 times>, "Ok         Read 8 bytes (was requested to read 16)",
                _IO_write_base = 0x646b90 "0Bd", ' ' <repeats 20 times>, "49  Nd", ' ' <repeats 62 times>, "Ok         Read 8 bytes (was requested to read 16)", _IO_write_ptr = 0x646beb "Ok         Read 8 bytes (was requested to read 16)", _IO_write_end = 0x646c20 "\240",
                _IO_buf_base = 0x646b90 "0Bd", ' ' <repeats 20 times>, "49  Nd", ' ' <repeats 62 times>, "Ok         Read 8 bytes (was requested to read 16)", _IO_buf_end = 0x646c20 "\240", _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x0, _fileno = 0,
                _flags2 = 0, _old_offset = 4151907146, _cur_column = 0, _vtable_offset = 0 '\000', _shortbuf = "", _lock = 0x0, _offset = 0,
                _codecvt = 0x0, _wide_data = 0x0, _freeres_list = 0x0, _freeres_buf = 0x7fff00000000, _freeres_size = 0, _mode = -1,
                _unused2 = "\377\177\000\000\000\000\000\024\000\000\000\000\260\260>\367\377\177\000"}, vtable = 0x7ffff7773fa0 <_IO_strn_jumps>},
            _s = {_allocate_buffer = 0x0, _free_buffer = 0x650450}},
          overflow_buf = "-\a\000\000\000\000\000\000\340\005e\000\000\000\000\000\"\000\000\000\000\000\000\000\"", '\000' <repeats 15 times>, "@Ww\367\377\177\000\000\222\000\000\000\000\000\000\000\060\030@\000\000\000\000"}
        ret = <optimized out>
#3  0x00007ffff7408142 in __snprintf (s=<optimized out>, maxlen=<optimized out>, format=<optimized out>) at snprintf.c:34
        arg = {{gp_offset = 48, fp_offset = 48, overflow_arg_area = 0x7fffffffddd0, reg_save_area = 0x7fffffffdd00}}
        done = 10
#4  0x0000000000401d88 in get_str_info (i=0x6441b0) at ui.c:13
        tmp_size = 146
        tmp = 0x646b90 "0Bd", ' ' <repeats 20 times>, "49  Nd", ' ' <repeats 62 times>, "Ok         Read 8 bytes (was requested to read 16)"
#5  0x0000000000402154 in draw_data (in=0x643dd0) at ui.c:118
        s_info = 0x646b90 "0Bd", ' ' <repeats 20 times>, "49  Nd", ' ' <repeats 62 times>, "Ok         Read 8 bytes (was requested to read 16)"
        i = 10
#6  0x00000000004028c3 in read_from_socket (sock_fd=7) at procmon-viewer.c:276
        x = 0x646f10
        i = 0x646e10
#7  0x0000000000402671 in main (argc=1, argv=0x7fffffffdfc8) at procmon-viewer.c:192
        n = 1
        i = 0
        ch = -1
        sock_fd = 7
        efd = 9
        stdin_fd = 8
        socket_event = {events = 1, data = {ptr = 0x40183000000007, fd = 7, u32 = 7, u64 = 18040992946978823}}
        stdin_event = {events = 1, data = {ptr = 0x8, fd = 8, u32 = 8, u64 = 8}}
        events = 0x63bb00

我完全迷路了。这是我第一次看到这样的回溯。为什么我会得到这个?

我将代码更新为:

int err;
char *tmp = malloc(win_data_width + 1);
if(!tmp){
    return NULL;
}

err = snprintf(tmp,
    win_data_width,
    "%-15.20s"       /* Process name */
    "%10u"           /* PID          */
    " %-15.10s"      /* Operation    */
    "%-50.45s"       /* Path         */
    "%-10.10s"       /* Result       */
    " %-200.200s\n", /* Details      */

    i->pname,
    i->pid,
    i->operation,
    i->path,
    i->result,
    i->details
);

if(err < 0){
    return NULL;
}

而且我仍然遇到完全相同的分段错误。我认为丢弃 malloc/snprintf 错误是合理的。还有什么可能导致这种情况?

i 是一个带有几个char * 和一个pid_t 的结构(不是指向pid_t 的指针)。

每个char * 在调用此函数之前都经过了malloc-ed,并且保证每个char * 都具有它应该包含的数据或空字符串。 也许i 中的一些char * 的大小可能大于win_data_width,但在这种情况下snprintf 应该截断内容(这对我的应用程序来说是可以接受的行为)。

也就是说:我不希望tmp 拥有ALL每个char * 的内容,而是根据win_data_width 的最大可能数据。

【问题讨论】:

  • 嗯,我们可以看看你的完整代码吗?
  • 我可以贴出来,但是涉及到的代码部分很多。无论如何,如果您真的想查看整个代码,我可以在 GitHub 中创建另一个分支并喜欢那里
  • 如果你觉得你需要这样做,你的问题可能对 StackOverflow 来说太复杂了。你能把范围缩小一点吗?
  • @ryyker 在我使用sNprintf 时应该没关系,对吧?我的意思是,它不会复制超过win_data_width 字节。
  • 注意:为确保兼容性,请将i-&gt;pid 转换为与您的格式说明符匹配的类型。 IOW (unsigned) i-&gt;pid.

标签: c segmentation-fault gdb


【解决方案1】:

你应该编码(如果用 C++ 编码)

 char *tmp = new char [tmp_size];

(甚至可能使用tmp_size+1

或(如果使用纯 C 编码)

 char* tmp = malloc(tmp_size);
 if (!tmp) { perror("malloc"); exit(EXIT_FAILURE); };

char *tmp = new(tmp_size); 可能不应该编译。还有

 char *tmp = new char(tmp_size);

(在 C++ 中)错误地在初始化为 tmp_size 的堆上分配了一个 char

请记住,new 是 C++ 中的关键字。避免在 C 中使用该词。在纯 C 中使用名为 new 的宏或函数是很糟糕的。

因为有些人只使用 C++ 编译器,即使是 C 代码。

在许多 C 程序中,总是成功 malloc(并在 malloc 失败时终止)的函数通常称为 xmalloc

static inline void* xmalloc(size_t sz) {
   void* p = malloc(sz);
   if (!p) { 
     fprintf(stderr, "xmalloc failed for %zd bytes: %s\n",
             sz, strerror(errno));
     exit(EXIT_FAILURE); 
   };
   return p;
}

不要在 C 中调用 new

顺便说一句,总是检查malloc 是否失败;使用内置的ulimit bash 降低内存限制以进行测试。

如果你的系统有asprintf(3),你应该考虑使用它。

如果您的代码崩溃,您可能已经损坏了堆内存(其他地方)。当然使用valgrind 应该会有所帮助。

【讨论】:

  • 你为什么建议它? malloc 还不够吗?
  • 哦,对不起,我看到了混乱。由坏。 new 是一个宏,它只调用 malloc
  • @alexandernst 这就是我们需要查看您的其余代码的原因!
  • 味道差,和bad form一样吗? OP 明确标记为 C,而不是 C++。
  • 我认为当 C++ 人员停止使用类和模板来确保他们的代码能够编译为 C 时,C 程序员应该担心在 C 程序中不使用像 new 这样的标识符。下一步是什么?避免使用变长数组,从malloc()开始转换返回值?
【解决方案2】:

我认为在调用 snprintf() 之前,您需要仔细查看所有结构成员的内存分配。如果有任何空指针,任何期望空终止符的字符串操作都将失败,可能会产生您的段错误。

int snprintf (char Target_String[], size_t Buffer_Size, const char Format_String[], ...);

具体如下:
snprintf()` - 根据 formatString 中的格式说明符将输出写入指定字符串。一个空字符写在所写字符的末尾。 (这里没有提到是否传递了空指针).

如果生成的格式化字符串超过 bufferSize - 1 个字节长(不包括终止的空字符),则只有第一个 bufferSize - 1 个字符加上空字符会写入 targetString。该函数始终返回格式化字符串的全长,只有在发生截断时才大于 bufferSize。

这就是为什么建议您检查snprintf() 的输出,总是(不仅仅是负值)。解决你调用这个特定函数的所有问题,我怀疑你的段错误不会发生。

#define WIN_DATA_WIDTH 0x0018 是我在 [此处] 找到的定义 9https://chromium.googlesource.com/chromiumos/third_party/kernel-next/+/refs/heads/master/include/pcmcia/cs.h0)。我不确定这是否是您的环境中的情况。 0x0018 是十进制的 24 字节。

[编辑]

按照我在下面说明的操作(我能做的最好的事情是看你如何为 struct i 的 char * 成员分配内存),我在这里没有段错误:在你的编译器中试试这个,看看你得到了什么. (当函数返回时,tmp 仅保留 24 个字节作为 malloc'ed,err 设置为 303,但没有段错误)另外,如果我将成员传递给 I 而不分配内存,struct I 会出现运行时错误, 在被初始化之前被引用,但这不是致命的。当然总是有段错误。

#include <ansi_c.h>

typedef struct {
    char *a;
    char *b;
    char *c;
    char *d;
    char *e;
    char *f;
    char *g;
}I;

int main(void)
{

        I k;
        k.a = malloc(24); sprintf(k.a, "%f", 12343.432524524);
        k.b = malloc(24); sprintf(k.b, "%f", 535.45645646464); 
        k.c = malloc(24); sprintf(k.c, "%f", 4.4564456645645); 
        k.d = malloc(24); sprintf(k.d, "%f", 456.644564564564); 
        k.e = malloc(24); sprintf(k.e, "%f", 5.4564564564564); 
        k.f = malloc(24); sprintf(k.f, "%f", 45.456456456456); 


    size_t tmp_size = 0x0018; // win_data_width is the width of a Ncurses window

    char *tmp = malloc(tmp_size);   // <------- new == malloc MACRO

        err = snprintf(tmp,
                tmp_size,
                "%-15.20s"       /* Process name */
                "%10u"           /* PID          */
                " %-15.10s"      /* Operation    */
                "%-50.45s"       /* Path         */
                "%-10.10s"       /* Result       */
                " %-200.200s\n", /* Details      */

                k.a,
                k.b,
                k.c,
                k.d,
                k.e,
                k.f
    );  ///////////////////////////////////////   
    return 0;
}

【讨论】:

  • i 是一个包含几个 char * 和一个 pid_t(不是指向 pid_t 的指针)的结构。 发布代码段,展示您如何准备struct 成员,在它们作为参数传递给snprintf() 之前。显然,如果您在调用此函数的执行流程中遇到段错误,则一个或多个参数有问题。对不起,我得离开一会儿。但是,如果您发布其他有意义的内容,我相信有人会使用它。
  • @alexandernst - 好的,我已经尽力重建场景,但没有看到你的结构是如何构建的,我没有遇到段错误。比较我拥有的和你拥有的,看看会出现什么。
猜你喜欢
  • 2015-10-14
  • 1970-01-01
  • 1970-01-01
  • 2011-03-10
  • 2017-06-28
  • 2012-08-11
  • 2011-10-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多