【问题标题】:segfault on exit(EXIT_FAILURE) but not with error(EXIT_FAILURE, ...)退出时的段错误(EXIT_FAILURE)但没有错误(EXIT_FAILURE,...)
【发布时间】:2013-12-18 21:14:19
【问题描述】:

我在一个程序 (source here) 中使用 getopt_long,并且在测试它的行为时给出了一个无效的长选项 (--stack-overflow),我得到:

[marcoms@baguette16 btcwatch]$ ./btcwatch --stack-overflow
Segmentation fault (core dumped)

对于上下文,它在getopt_long() 循环中,即:

while((opt = getopt_long(argc,
                         argv,
                         OPTSTRING,
                         long_options,
                         &longopt_i)) != -1)
{

    ...

    default:
        exit(EXIT_FAILURE);
        break;

代替:

error(EXIT_FAILURE, 0, "unknown option !?");

exit() 代码SIGSEGVs)

疯狂的是,default 不应该被(根据gdb 的说法也不应该)被执行。

gdb 表明它在getopt_long() 调用时立即崩溃,

(gdb) start --stack-overflow    
Temporary breakpoint 1, main (argc=2, argv=0x7fffffffe598) at src/main.c:96
96      btcdbg("main()");
(gdb) s
btcdbg (fmt=0x403260 "main()") at src/btcutil.c:50
50  }
(gdb) 
main (argc=2, argv=0x7fffffffe598) at src/main.c:118
118     const struct option long_options[] = {
(gdb) 
211     api_err.err = false;
(gdb) 
212     colour = false;
(gdb) 
213     found_path = false;
(gdb) 
214     fp = NULL;
(gdb) 
215     n = 1.0;
(gdb) 
216     newlp = NULL;
(gdb) 
217     pn = argv[0];
(gdb) 
218     reverse = false;
(gdb) 
219     verbose = false;
(gdb) 
221     strcpy(currcy, "USD");
(gdb) 
223     setlocale(LC_ALL, "");  // sets the locale to the system's default
(gdb) 
225     while((opt = getopt_long(
(gdb) 

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7643e2c in __strncmp_sse2 () from /usr/lib/libc.so.6

但是当它使用error() 而不是exit() 运行时,它会正常运行:

(gdb) start --stack-overflow
Temporary breakpoint 1, main (argc=2, argv=0x7fffffffe598) at src/main.c:96
96      btcdbg("main()");
(gdb) s
btcdbg (fmt=0x4032a0 "main()") at src/btcutil.c:50
50  }
(gdb) 
main (argc=2, argv=0x7fffffffe598) at src/main.c:118
118     const struct option long_options[] = {
(gdb) 
211     api_err.err = false;
(gdb) 
212     colour = false;
(gdb) 
213     found_path = false;
(gdb) 
214     fp = NULL;
(gdb) 
215     n = 1.0;
(gdb) 
216     newlp = NULL;
(gdb) 
217     pn = argv[0];
(gdb) 
218     reverse = false;
(gdb) 
219     verbose = false;
(gdb) 
221     strcpy(currcy, "USD");
(gdb) 
223     setlocale(LC_ALL, "");  // sets the locale to the system's default
(gdb) 
225     while((opt = getopt_long(
(gdb) 
/home/marcoms/code/btcwatch/./btcwatch: unrecognized option '--stack-overflow'
232         btcdbg("got option '%c'", opt);
(gdb) 
btcdbg (fmt=0x4032a8 "got option '%c'") at src/btcutil.c:50
50  }
(gdb) 
main (argc=2, argv=0x7fffffffe598) at src/main.c:233
233         switch(opt) {
(gdb) 
236                 help(pn, optarg);
(gdb) 
help (prog_nm=0x7fffffffe8d8 "/home/marcoms/code/btcwatch/./btcwatch", topic=0x0) at src/btcutil.c:69
69      btcdbg("help()");
(gdb) 
btcdbg (fmt=0x403bfc "help()") at src/btcutil.c:50
50  }
(gdb) 
help (prog_nm=0x7fffffffe8d8 "/home/marcoms/code/btcwatch/./btcwatch", topic=0x0) at src/btcutil.c:71
71      char currcies[][3 + 1] = {
(gdb) 
101     char topics[][16] = {
(gdb) 
117     if(!topic) {
(gdb) 
118         bputs("Usage: "); bputs(prog_nm); bputs(" [OPTION]\n");
(gdb) 
Usage: /home/marcoms/code/btcwatch/./btcwatch [OPTION]
119         bputs(
(gdb) 
Get and monitor Bitcoin trade information

Options:       Long options:
  -C             --compare              comare current price with stored price
  -S             --store                store current price
  -a             --all                  equivalent to -pbs
  -b             --buy                  print buy price
  -c CURRENCY    --currency=CURRENCY    set conversion currency
  -n AMOUNT      --amount=AMOUNT        set the amount to convert
  -o             --colour, --color      enable use of colour
  -p             --ping                 check for a successful JSON response
  -r             --reverse              convert currency to Bitcoin
  -s             --sell                 print sell price
  -v             --verbose              increase verbosity

  -h [topic]     --help[=topic]         print this help, or help designated by topic
                                        use --help=topics for available topics
  -V             --version              print version number

Report bugs to marco@scannadinari.co.uk
btcwatch home page: <https://github.com/marcoms/btcwatch>
142         exit(EXIT_SUCCESS);
(gdb) 
[Inferior 1 (process 25752) exited normally]

在这一点上,我真的很难过。什么可能导致段错误?当然,我可以只使用error() 而不是退出,但这令人难以置信。

【问题讨论】:

标签: c gdb segmentation-fault getopt


【解决方案1】:

问题是您的选项数组未正确终止:

    const struct option long_options[] = {
            // ...
            // This is the last element
            {
                    .name = "verbose",
                    .has_arg = no_argument,
                    .flag = NULL,
                    .val = 'v'
            }
    };

getopt_long(3) 要求选项数组以全零结束,以便它知道数组有多大——请注意,您实际上从未将数组大小传递给getopt_long。所以发生的事情是它走过你的数组的末尾,寻找终结符,然后它开始越界读取内存,Valgrind 正确地抱怨了这一点。未定义的行为结果。

修复很简单:

const struct option long_options[] = {
            // ...
            {
                    .name = "verbose",
                    .has_arg = no_argument,
                    .flag = NULL,
                    .val = 'v'
            },

            // Array terminator
            {0, 0, 0, 0}
};

【讨论】:

  • @marcoms:是的,它有点隐藏在文档中,所以很容易错过。每当您将数组传递给函数时,如果该函数不采用数组长度,您应该始终保持警惕——这通常表明正在发生一些不寻常的事情。如果getopt_long 将数组长度作为参数而不是使用哨兵,我个人更喜欢它,但显然更改 API 为时已晚。
猜你喜欢
  • 2012-11-19
  • 1970-01-01
  • 2019-06-24
  • 2022-10-08
  • 1970-01-01
  • 1970-01-01
  • 2015-01-24
  • 2011-10-26
  • 1970-01-01
相关资源
最近更新 更多