如何使用strncat()在运行时避免Abort Trap 6错误?

时间:2019-01-05 17:29:09

标签: c

中止陷阱6 问题源于调用extra_info()方法,该方法多次使用strncat()。删除此功能将不会在运行时产生任何错误。

据我了解:

  

中止陷阱:6是由于使用   指向不存在的内存位置的无效索引   Abort trap: 6 in C Program。   当需要释放可变内存时,也可能发生这种情况。避免   在这种情况下,您可以使用多个变量或释放单个变量   每次重新使用该变量。但是我感觉解决方案要简单得多。

#include <stdio.h>
#include <string.h>

char line[1001]; // The line supports up to a 1000 characters
char lines[11][1001]; // An array of lines (up to 10 lines where each line is a 1000 characters max)
char info[100]; // Holds extra info provided by user

char * extra_info(
        char string_1[],
        char string_2[],
        char string_3[],
        char string_4[],
        char string_5[]
    );

int main(){

    int 
    i, // Line number
    j; // Length of the line
    char result[100], text[100];
    FILE *file;

    strcpy(text, "String No."); // The default text

    file = fopen("test.txt", "w+"); // Open the file for reading and writing

    for(i = 0; i < 10; i++){ // Loop to create a line.

        if(i != 9){ // If the line is NOT at the 10th string

            sprintf(result, "%s%d, ", text, i); // Format the text and store it in result

        }
        else{

            sprintf(result, "%s%d ", text, i); // Format the text and store it in result            

        }

        extra_info(
            "st",
            "nd",
            "rd",
            "th",
            "th"
        );

        strncat(line, info, 100); // Append the extra info at the end of each line        

        printf("%s", result); // Display the result variable to the screen

        strncat(line, result, 15); // Concatenate all strings in one line

    }

    strncat(line, "\n\n", 2); // Add a new-line character at the end of each line

    for(j = 0; j < 10; j++){ // Now loop to change the line

        strcpy(lines[i], line); // Copy the line of text into each line of the array

        fputs(lines[i], file); // Put each line into the file        

    }

    fclose(file);  

}

char * extra_info( // Append user defined and predefined info at the end of a line
        char string_1[],
        char string_2[],
        char string_3[],
        char string_4[],
        char string_5[]
    ){
        char text[100]; // A variable to hold the text

        /* Append a default text into each strings 
        and concatenate them into one line */

        sprintf(text, " 1%s", string_1);
        strncat(line, text, 100);

        sprintf(text, ", 2%s", string_2);
        strncat(line, text, 100);

        sprintf(text, ", 3%s", string_3);
        strncat(line, text, 100);

        sprintf(text, ", 4%s", string_4);
        strncat(line, text, 100);

        sprintf(text, ", 5%s.", string_5);
        strncat(line, text, 100);

        strcpy(info, line); // Copies the line into the info global variable

        return line;

}

此代码可以使用GCC很好地编译,但是我偶然发现了代码可以正常工作的情况,但是由于该错误可能会破坏某些功能。这与以这种方式多次调用strncat()有关,这使我想到会有内存分配问题,但是在尝试其他示例之后,解决方案似乎简单得多。任何帮助,将不胜感激。预先感谢。

2 个答案:

答案 0 :(得分:2)

我在2018年3月编写了随附的代码,以使自己对strncat()所发生的另一个问题感到满意,这是我在提交答案之前删除的另一个问题。这只是重新定位该代码。

strncat()功能(如我在comment中所述)是邪恶的。它也与strncpy()界面不一致-与您在其他任何地方遇到的任何事物都不同。阅读完此内容后,您将(幸运的话)决定永远不要使用strncat()

TL; DR-永远不要使用strncat()

C标准定义了strncat()(并且POSIX同意-strncat()

  

C11§7.24.3.2strncat函数

     

简介

#include <string.h>
char *strncat(char * restrict s1, const char * restrict s2, size_t n);
     

说明

     

strncat函数从由n指向的数组到字符串末尾追加不超过s2个字符(不附加空字符和其后的字符) s1指向。 s2的初始字符将覆盖s1结尾的空字符。 309)如果在重叠的对象之间进行复制,则行为是不确定的。

     

返回

     

strncat函数返回s1的值。

     

309)因此,s1所指向的数组中可以结尾的最大字符数为strlen(s1)+n+1

脚注标识着strncat()的最大陷阱-您不能安全使用:

char *source = …;

char target[100] = "";

strncat(target, source, sizeof(target));

这与大多数其他函数在C代码中采用数组大小​​参数 1 的情况相反。

要安全使用strncat(),您应该知道:

  • target
  • sizeof(target) —或者,对于动态分配的空间,分配的长度
  • strlen(target)-您必须知道目标字符串中已经存在的长度
  • source
  • strlen(source) —如果您担心源字符串是否被截断;如果您不在意,则不需要

有了这些信息,您可以使用:

strncat(target, source, sizeof(target) - strlen(target) - 1);

但是,这样做有点愚蠢;如果您知道strlen(target),可以避免使用以下方法让strncat()再次发现它:

strncat(target + strlen(target), source, sizeof(target) - strlen(target) - 1);

请注意,与strncpy()不同,strncat()保证了空终止。这意味着您可以使用:

size_t t_size = sizeof(target);
size_t t_length = strlen(target);
strncpy(target + t_length, source, t_size - t_length - 1);
target[t_size - 1] = '\0';

,如果源字符串太长而无法追加到目标,则可以保证得到相同的结果。

演示代码

说明strncat()各个方面的多个程序。请注意,在macOS上,strncat()中有一个<string.h>的宏定义,该宏调用一个不同的函数__builtin___strncat_chk-来验证strncat()的使用。为了使命令行更简洁,我删除了通常使用的两个警告编译器选项--Wmissing-prototypes -Wstrict-prototypes-但这并不影响任何编译。

strncat19.c

这演示了strncat()的一种安全用法:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char spare1[16] = "abc";
    char buffer[16] = "";
    char spare2[16] = "xyz";
    strncat(buffer, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sizeof(buffer) - 1);
    printf("%zu: [%s]\n", strlen(buffer), buffer);
    printf("spare1 [%s]\n", spare1);
    printf("spare2 [%s]\n", spare2);
    return 0;
}

即使设置了严格的警告,它也可以干净地编译(使用XCode 10.1(clang的Apple Apple LLVM version 10.0.0 (clang-1000.11.45.5))和GCC 8.2.0:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror strncat19.c -o strncat19
$ ./strncat19
15: [ABCDEFGHIJKLMNO]
spare1 [abc]
spare2 [xyz]
$

strncat29.c

这类似于strncat19.c,但是(a)允许您指定要在命令行上复制的字符串,并且(b)错误地使用sizeof(buffer)而不是sizeof(buffer) - 1长度。

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
    const char *data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    if (argc == 2)
        data = argv[1];
    char spare1[16] = "abc";
    char buffer[16] = "";
    char spare2[16] = "xyz";
    strncat(buffer, data, sizeof(buffer));
    printf("%zu: [%s]\n", strlen(buffer), buffer);
    printf("spare1 [%s]\n", spare1);
    printf("spare2 [%s]\n", spare2);
    return 0;
}

此代码无法使用严格的警告选项进行编译:

$ clang -O3 -g -std=c11 -Wall -Wextra -Werror strncat29.c -o strncat29  
strncat29.c:12:27: error: the value of the size argument in 'strncat' is too large, might lead to a buffer
      overflow [-Werror,-Wstrncat-size]
    strncat(buffer, data, sizeof(buffer));
                          ^~~~~~~~~~~~~~
strncat29.c:12:27: note: change the argument to be the free space in the destination buffer minus the terminating null byte
    strncat(buffer, data, sizeof(buffer));
                          ^~~~~~~~~~~~~~
                          sizeof(buffer) - strlen(buffer) - 1
1 error generated.
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror strncat29.c -o strncat29  
In file included from /usr/include/string.h:190,
                 from strncat29.c:2:
strncat29.c: In function ‘main’:
strncat29.c:12:5: error: ‘__builtin___strncat_chk’ specified bound 16 equals destination size [-Werror=stringop-overflow=]
     strncat(buffer, data, sizeof(buffer));
     ^~~~~~~
cc1: all warnings being treated as errors
$

即使没有请求警告,警告也是由GCC发出的,但是由于没有-Werror选项,它会生成可执行文件:

$ gcc -o strncat29 strncat29.c
In file included from /usr/include/string.h:190,
                 from strncat29.c:2:
strncat29.c: In function ‘main’:
strncat29.c:12:5: warning: ‘__builtin___strncat_chk’ specified bound 16 equals destination size [-Wstringop-overflow=]
     strncat(buffer, data, sizeof(buffer));
     ^~~~~~~
$ ./strncat29
Abort trap: 6
$ ./strncat29 ZYXWVUTSRQPONMK
15: [ZYXWVUTSRQPONMK]
spare1 [abc]
spare2 [xyz]
$ ./strncat29 ZYXWVUTSRQPONMKL
Abort trap: 6
$

这是__builtin__strncat_chk函数在起作用。

strncat97.c

此代码还带有一个可选的字符串参数;它还要注意命令行上是否还有另一个参数,如果是,它会直接调用strncat()函数,而不是先让宏对其进行检查:

#include <stdio.h>
#include <string.h>

/*
** Demonstrating that strncat() should not be given sizeof(buffer) as
** the size, even if the string is empty to start with.  The use of
** (strncat) inhibits the macro expansion on macOS; the code behaves
** differently when the __strncat_chk function (on High Sierra or
** earlier - it's __builtin__strncat_chk on Mojave) is called instead.
** You get an abort 6 (but no other useful message) when the buffer
** length is too long.
*/

int main(int argc, char **argv)
{
    const char *data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    if (argc >= 2)
        data = argv[1];
    char spare1[16] = "abc";
    char buffer[16] = "";
    char spare2[16] = "xyz";
    size_t len = (argc == 2) ? sizeof(buffer) : sizeof(buffer) - 1;
    if (argc < 3)
        strncat(buffer, data, len);
    else
        (strncat)(buffer, data, len);
    printf("buffer %2zu: [%s]\n", strlen(buffer), buffer);
    printf("spare1 %2zu: [%s]\n", strlen(spare1), spare1);
    printf("spare2 %2zu: [%s]\n", strlen(spare2), spare2);
    return 0;
}

现在,编译器产生不同的结果:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror strncat97.c -o strncat97  
strncat97.c: In function ‘main’:
strncat97.c:26:9: error: ‘strncat’ output truncated copying 15 bytes from a string of length 26 [-Werror=stringop-truncation]
         (strncat)(buffer, data, len);
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
$ clang -O3 -g -std=c11 -Wall -Wextra -Werror strncat97.c -o strncat97  
$

这展示了使用多个编译器的优势-不同的编译器有时会发现不同的问题。尝试使用不同数量的选项来执行多项操作,此代码很混乱。足以显示:

$ ./strncat97
0x7ffee7506420: buffer 15: [ABCDEFGHIJKLMNO]
0x7ffee7506430: spare1  3: [abc]
0x7ffee7506410: spare2  3: [xyz]
$ ./strncat97 ABCDEFGHIJKLMNOP
Abort trap: 6
$ ./strncat97 ABCDEFGHIJKLMNO
0x7ffeea141410: buffer 15: [ABCDEFGHIJKLMNO]
0x7ffeea141420: spare1  3: [abc]
0x7ffeea141400: spare2  3: [xyz]
$

strncat37.c

这是上面所有程序的完整版本,并且通过getopt()处理选项。它还使用我的错误报告例程;它们的代码在我的GitHub上的SOQ(堆栈溢出问题)存储库中,作为src/libsoq子目录中的文件stderr.cstderr.h

#include "stderr.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>

/*
** Demonstrating that strncat() should not be given sizeof(buffer) as
** the size, even if the string is empty to start with.  The use of
** (strncat) inhibits the macro expansion on macOS; the code behaves
** differently when the __strncat_chk function (on High Sierra or
** earlier - it's __builtin__strncat_chk on Mojave) is called instead.
** You get an abort 6 (but no other useful message) when the buffer
** length is too long.
*/

static const char optstr[] = "fhlmsV";
static const char usestr[] = "[-fhlmsV] [string]";
static const char hlpstr[] =
    "  -f  Function is called directly\n"
    "  -h  Print this help message and exit\n"
    "  -l  Long buffer length -- sizeof(buffer)\n"
    "  -m  Macro cover for the function is used (default)\n"
    "  -s  Short buffer length -- sizeof(buffer)-1 (default)\n"
    "  -V  Print version information and exit\n"
    ;

int main(int argc, char **argv)
{
    err_setarg0(argv[0]);

    int f_flag = 0;
    int l_flag = 0;
    int opt;

    while ((opt = getopt(argc, argv, optstr)) != -1)
    {
        switch (opt)
        {
        case 'f':
            f_flag = 1;
            break;
        case 'h':
            err_help(usestr, hlpstr);
            /*NOTREACHED*/
        case 'l':
            l_flag = 1;
            break;
        case 'm':
            f_flag = 0;
            break;
        case 's':
            l_flag = 0;
            break;
        case 'V':
            err_version(err_getarg0(), &"@(#)$Revision$ ($Date$)"[4]);
            /*NOTREACHED*/
        default:
            err_usage(usestr);
            /*NOTREACHED*/
        }
    }

    if (optind < argc - 1)
        err_usage(usestr);

    const char *data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    if (optind != argc)
        data = argv[optind];
    char spare1[16] = "abc";
    char buffer[16] = "";
    char spare2[16] = "xyz";
    size_t len = l_flag ? sizeof(buffer) : sizeof(buffer) - 1;

    printf("Specified length: %zu\n", len);
    printf("Copied string: [%s]\n", data);
    printf("Copied %s\n", f_flag ? "using strncat() function directly"
                                 : "using strncat() macro");

    if (f_flag)
        (strncat)(buffer, data, len);
    else
        strncat(buffer, data, len);

    printf("%p: buffer %2zu: [%s]\n", (void *)buffer, strlen(buffer), buffer);
    printf("%p: spare1 %2zu: [%s]\n", (void *)spare1, strlen(spare1), spare1);
    printf("%p: spare2 %2zu: [%s]\n", (void *)spare2, strlen(spare2), spare2);
    return 0;
}

和以前一样,Clang和GCC对代码的可接受性有不同的看法(-Werror表示来自GCC的警告被视为错误):

$ clang -O3 -g -I./inc -std=c11 -Wall -Wextra -Werror strncat37.c -o strncat37 -L./lib  -lsoq 
$ gcc -O3 -g -I./inc -std=c11 -Wall -Wextra -Werror strncat37.c -o strncat37 -L./lib  -lsoq 
strncat37.c: In function ‘main’:
strncat37.c:80:9: error: ‘strncat’ output may be truncated copying between 15 and 16 bytes from a string of length 26 [-Werror=stringop-truncation]
         (strncat)(buffer, data, len);
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
$

运行时:

$ ./strncat37 -h
Usage: strncat37 [-fhlmsV] [string]
  -f  Function is called directly
  -h  Print this help message and exit
  -l  Long buffer length -- sizeof(buffer)
  -m  Macro cover for the function is used (default)
  -s  Short buffer length -- sizeof(buffer)-1 (default)
  -V  Print version information and exit

$ ./strncat37
Specified length: 15
Copied string: [ABCDEFGHIJKLMNOPQRSTUVWXYZ]
Copied using strncat() macro
0x7ffedff4e400: buffer 15: [ABCDEFGHIJKLMNO]
0x7ffedff4e410: spare1  3: [abc]
0x7ffedff4e3f0: spare2  3: [xyz]
$ ./strncat37 -m -s
Specified length: 15
Copied string: [ABCDEFGHIJKLMNOPQRSTUVWXYZ]
Copied using strncat() macro
0x7ffeeaf043f0: buffer 15: [ABCDEFGHIJKLMNO]
0x7ffeeaf04400: spare1  3: [abc]
0x7ffeeaf043e0: spare2  3: [xyz]
$ ./strncat37 -m -l
Specified length: 16
Copied string: [ABCDEFGHIJKLMNOPQRSTUVWXYZ]
Copied using strncat() macro
Abort trap: 6
$ ./strncat37 -f -s
Specified length: 15
Copied string: [ABCDEFGHIJKLMNOPQRSTUVWXYZ]
Copied using strncat() function directly
0x7ffeef0913f0: buffer 15: [ABCDEFGHIJKLMNO]
0x7ffeef091400: spare1  3: [abc]
0x7ffeef0913e0: spare2  3: [xyz]
$ ./strncat37 -f -l
Specified length: 16
Copied string: [ABCDEFGHIJKLMNOPQRSTUVWXYZ]
Copied using strncat() function directly
0x7ffeed8d33f0: buffer 16: [ABCDEFGHIJKLMNOP]
0x7ffeed8d3400: spare1  0: []
0x7ffeed8d33e0: spare2  3: [xyz]
$

默认行为也是正确的行为;该程序不会崩溃,也不会产生意外的副作用。当使用宏运行并且指定的长度太长(-m -l)时,程序崩溃。当使用该函数并且长度太长(-f -l)运行时,程序将在spare1末尾添加空值的情况下覆盖数组buffer的第一个字节,并显示16个字节的数据而不是15。


1 当您使用scanf()或类似方法时,%31s中有一个例外;指定的数字是可以存储在字符串中的非空字符的数目;读取其他31个字符后,它将添加一个空字节。同样,可以安全使用的最大大小为sizeof(string) - 1

您可以在我的GitHub上的SOQ子目录的src/so-5405-4423(堆栈溢出问题)存储库中找到strncatXX.c的代码。


问题代码分析

从问题中获取代码,并将int main(){更改为int main(void){,因为我的默认编译选项会生成错误(如果我不使用-Werror,则会出现警告)非原型main(),并在return 0;的末尾添加main(),剩下的就是这些错误,这些错误使我在运行macOS 10.14.2 Mojave的Mac上使用GCC 8.2.0进行编译:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes so-5405-4423-v1.c -o so-5405-4423-v1 
In file included from /opt/gcc/v8.2.0/lib/gcc/x86_64-apple-darwin17.7.0/8.2.0/include-fixed/stdio.h:425,
                 from so-5405-4423-v1.c:1:
so-5405-4423-v1.c: In function ‘main’:
so-5405-4423-v1.c:32:29: error: ‘%d’ directive writing between 1 and 2 bytes into a region of size between 1 and 100 [-Werror=format-overflow=]
             sprintf(result, "%s%d, ", text, i); // Format the text and store it in result
                             ^~~~~~~~
so-5405-4423-v1.c:32:29: note: directive argument in the range [0, 10]
so-5405-4423-v1.c:32:13: note: ‘__builtin___sprintf_chk’ output between 4 and 104 bytes into a destination of size 100
             sprintf(result, "%s%d, ", text, i); // Format the text and store it in result
             ^~~~~~~
so-5405-4423-v1.c:37:29: error: ‘ ’ directive writing 1 byte into a region of size between 0 and 99 [-Werror=format-overflow=]
             sprintf(result, "%s%d ", text, i); // Format the text and store it in result
                             ^~~~~~~
so-5405-4423-v1.c:37:13: note: ‘__builtin___sprintf_chk’ output between 3 and 102 bytes into a destination of size 100
             sprintf(result, "%s%d ", text, i); // Format the text and store it in result
             ^~~~~~~
cc1: all warnings being treated as errors
$

编译器注意到text是一个可以包含0到99个字符的字符串,因此从理论上讲,当与数字和", "(或{{1} }进行一次迭代)。将其初始化为" "的事实意味着不存在溢出风险,但是您可以通过为"String No."使用较短的长度来缓解这种情况,例如用text代替{{1 }}。

我承认,这个警告在GCC中是相对较新的,并不总是那么有用(这是代码正常的情况,但是警告仍然出现)。我通常会来解决该问题,仅是因为该问题当前会以我的默认选项显示,并且代码无法使用20进行任何警告编译,并且我不准备这样做保护等级。我不使用100的{​​{1}}选项raw;它会产生绝对适得其反的警告(至少是AFAIAC)。但是,我反对对我不起作用的“所有”选项。如果出于某种原因,-Werrorclang选项太痛苦了,我会反抗,但要谨慎。我会检查疼痛程度,并针对任何症状进行处理。

您也有循环:

-Weverything

不幸的是,运行此循环时,-Wall等于-Wextra,它超出了数组for(j = 0; j < 10; j++){ // Now loop to change the line strcpy(lines[i], line); // Copy the line of text into each line of the array fputs(lines[i], file); // Put each line into the file } 的范围。这可能导致崩溃。假定索引应该是i而不是10

这是您的代码的测试版本(lines

j

运行时,它产生的输出类似于:

i

当您发现214个字节从so-5405-4423-v2.c(足够容纳该字符串)被复制到#include <stdio.h> #include <string.h> char line[1001]; char lines[11][1001]; char info[100]; char *extra_info(char string_1[], char string_2[], char string_3[], char string_4[], char string_5[]); int main(void) { char result[100], text[20]; const char filename[] = "test.txt"; FILE *file; strcpy(text, "String No."); file = fopen(filename, "w+"); if (file == NULL) { fprintf(stderr, "Failed to open file '%s' for writing/update\n", filename); return 1; } for (int i = 0; i < 10; i++) { if (i != 9) sprintf(result, "%s%d, ", text, i); else sprintf(result, "%s%d ", text, i); fprintf(stderr, "Iteration %d:\n", i); fprintf(stderr, "1 result (%4zu): [%s]\n", strlen(result), result); fprintf(stderr, "1 line (%4zu): [%s]\n", strlen(line), line); extra_info("st", "nd", "rd", "th", "th"); fprintf(stderr, "2 line (%4zu): [%s]\n", strlen(line), line); fprintf(stderr, "1 info (%4zu): [%s]\n", strlen(info), info); strncat(line, info, 100); fprintf(stderr, "3 line (%4zu): [%s]\n", strlen(line), line); printf("%s", result); strncat(line, result, 15); fprintf(stderr, "3 line (%4zu): [%s]\n", strlen(line), line); } fprintf(stderr, "4 line (%4zu): [%s]\n", strlen(line), line); strncat(line, "\n\n", 2); for (int j = 0; j < 10; j++) { strcpy(lines[j], line); fputs(lines[j], file); } fclose(file); return 0; } char *extra_info(char string_1[], char string_2[], char string_3[], char string_4[], char string_5[]) { char text[100]; sprintf(text, " 1%s", string_1); fprintf(stderr, "EI 1: add (%zu) [%s] to (%zu) [%s]\n", strlen(string_1), string_1, strlen(line), line); strncat(line, text, 100); sprintf(text, ", 2%s", string_2); fprintf(stderr, "EI 2: add (%zu) [%s] to (%zu) [%s]\n", strlen(string_2), string_2, strlen(line), line); strncat(line, text, 100); sprintf(text, ", 3%s", string_3); fprintf(stderr, "EI 3: add (%zu) [%s] to (%zu) [%s]\n", strlen(string_3), string_3, strlen(line), line); strncat(line, text, 100); sprintf(text, ", 4%s", string_4); fprintf(stderr, "EI 4: add (%zu) [%s] to (%zu) [%s]\n", strlen(string_4), string_4, strlen(line), line); strncat(line, text, 100); sprintf(text, ", 5%s.", string_5); fprintf(stderr, "EI 5: add (%zu) [%s] to (%zu) [%s]\n", strlen(string_5), string_5, strlen(line), line); strncat(line, text, 100); fprintf(stderr, "EI 6: copy (%zu) [%s] to info\n", strlen(line), line); strcpy(info, line); return line; } (不是—它长100个字节)时,随之而来的崩溃是并不奇怪。尚不清楚所需的行为是什么。

在我的Mac上,Iteration 0: 1 result ( 13): [String No.0, ] 1 line ( 0): [] EI 1: add (2) [st] to (0) [] EI 2: add (2) [nd] to (4) [ 1st] EI 3: add (2) [rd] to (9) [ 1st, 2nd] EI 4: add (2) [th] to (14) [ 1st, 2nd, 3rd] EI 5: add (2) [th] to (19) [ 1st, 2nd, 3rd, 4th] EI 6: copy (25) [ 1st, 2nd, 3rd, 4th, 5th.] to info 2 line ( 25): [ 1st, 2nd, 3rd, 4th, 5th.] 1 info ( 25): [ 1st, 2nd, 3rd, 4th, 5th.] 3 line ( 50): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.] 3 line ( 63): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, ] Iteration 1: 1 result ( 13): [String No.1, ] 1 line ( 63): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, ] EI 1: add (2) [st] to (63) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, ] EI 2: add (2) [nd] to (67) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st] EI 3: add (2) [rd] to (72) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd] EI 4: add (2) [th] to (77) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd] EI 5: add (2) [th] to (82) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th] EI 6: copy (88) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.] to info 2 line ( 88): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.] 1 info ( 88): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.] 3 line ( 176): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.] 3 line ( 189): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, ] Iteration 2: 1 result ( 13): [String No.2, ] 1 line ( 189): [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, ] EI 1: add (2) [st] to (189) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, ] EI 2: add (2) [nd] to (193) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, 1st] EI 3: add (2) [rd] to (198) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, 1st, 2nd] EI 4: add (2) [th] to (203) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, 1st, 2nd, 3rd] EI 5: add (2) [th] to (208) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, 1st, 2nd, 3rd, 4th] EI 6: copy (214) [ 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th. 1st, 2nd, 3rd, 4th, 5th.String No.0, 1st, 2nd, 3rd, 4th, 5th.String No.1, 1st, 2nd, 3rd, 4th, 5th.] to info String No.0, String No.1, Abort trap: 6 调试器在line中报告崩溃; AFAICT,它位于info末尾突出显示的行中:

lldb

因此,虽然显然不是__strcpy_chk导致崩溃,但是extra_info() 的使用方式显然不是正确的-IMO,这是不正确的,但是看法可能会有所不同。我仍然坚持我的基本结论:请勿使用frame #6: 0x00007fff681bbe84 libsystem_c.dylib`__strcpy_chk + 83 frame #7: 0x00000001000017cc so-5405-4423-v2`extra_info(string_1=<unavailable>, string_2=<unavailable>, string_3="rd", string_4="th", string_5="th") at so-5405-4423-v2.c:86

答案 1 :(得分:-1)

解决方案很简单,因为我感觉到,在C语言中根本没有邪恶,邪恶或愤世嫉俗的现象。所有strcpy()的第1个都不需要发生,第2个extra_info()出于其目的而被放错了位置,第3个即使我要使用strcpy()来交换参数也是如此。因此,错误中止陷阱6

<img src="//colinbendell.cloudinary.com/image/upload/c_crop,f_auto,g_auto,h_350,w_400/v1512090971/Wizard-Clap-by-Markus-Magnusson.gif" id="gif1" alt="1.gif" style="width:400px;height:400px;border:1px solid black;" />