意外的跟踪陷阱

Unexpected trace trap

我有以下代码:

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

#define MAX_FIL 15
#define MAX_COL 15
#define MAX 15*15


int frase_a_taula(char frase[], char taula[MAX_FIL][MAX_COL]) {
    int i, j, k = 0, l, res = 0, final = 0;
    for (i = 0; !final; i++) {
        l = 0;
        for (j = i; frase[j] != ' ' && frase[j] != '.'; j++) {
            taula[k][l] = frase[j];
            l++;
        }
        taula[k][l] = '[=10=]';
        if (frase[j] == '.') final = 1;
        i = j;
        k++;
        res++;
    }
    return res;
}


void mescla_string_taula(char taula[MAX_FIL][MAX_COL], int n_filas, int coeficient) {
    int n_mescles = 0, i, ind1, ind2;
    if (coeficient == 2) {
        n_mescles = n_filas / 2;
    }
    else if (coeficient == 1) {
        n_mescles = n_filas / 4;
    }

    srand(time(NULL));
    for (i = 0; i < n_mescles; i++) {
        ind1 = rand() % n_filas;
        ind2 = rand() % n_filas;
        char *temp = (char *)malloc((strlen(taula[ind1]) + 1) * sizeof(char));
        strcpy(temp, taula[ind1]);
        strcpy(taula[ind1], taula[ind2]);
        strcpy(taula[ind2], temp);
        free(temp);
    }

}

void alenteix_i_mescla_frase(char frase[MAX], char frase_lenta[MAX], 
                             int velocitat, int coeficient) {
    char taula[MAX_FIL][MAX_COL];
    int i, j, len_frase_lenta;
    int n_paraules = frase_a_taula(frase, taula);
    mescla_string_taula(taula, n_paraules, coeficient);

    
    strcpy(frase_lenta, taula[0]);

    len_frase_lenta = strlen(frase_lenta);
    for (i = 1; i < n_paraules; i++) {
        if (velocitat == 1) {
            if (i % 2 == 0) {
                frase_lenta[len_frase_lenta] = '.';
                frase_lenta[len_frase_lenta + 1] = '.';
                frase_lenta[len_frase_lenta + 2] = '.';
                len_frase_lenta += 3;
                for (j = 0; j < strlen(taula[i]); j++) {
                    frase_lenta[len_frase_lenta] = taula[i][j];
                    len_frase_lenta++;
                }
            }
            else {
                frase_lenta[len_frase_lenta] = ' ';
                len_frase_lenta++;
                for (j = 0; j < strlen(taula[i]); j++) {
                    frase_lenta[len_frase_lenta] = taula[i][j];
                    len_frase_lenta++;
                }
            }
        }
        else if (velocitat == 2) {
            frase_lenta[len_frase_lenta] = '.';
            frase_lenta[len_frase_lenta + 1] = '.';
            frase_lenta[len_frase_lenta + 2] = '.';
            len_frase_lenta += 3;
            for (j = 0; j < strlen(taula[i]); j++) {
                frase_lenta[len_frase_lenta] = taula[i][j];
                len_frase_lenta++;
            }
        }
    }
    frase_lenta[len_frase_lenta] = '.';
    frase_lenta[len_frase_lenta+1] = '[=10=]';
    
}


int main() {
    printf("Introdueix nom del fitxer: ");
    char path[MAX];
    scanf("%s", path);
    printf("Introdueix velocitat de parla: ");
    int velocitat;
    scanf("%d", &velocitat);
    printf("Introdueix el coeficient de yodificacio: ");
    int coef;
    scanf("%d", &coef);
    FILE* f = fopen(path, "r");
    if (f == NULL) {
        printf("Fitxer no trobat!");
    }
    else {
        char line[MAX], frase_yodificada[MAX];
        while (fgets(line, MAX, f) != NULL) {
            alenteix_i_mescla_frase(line, frase_yodificada, velocitat, coef);
            printf("%s\n", frase_yodificada);
        }
        printf("De processar...ha acabat, el fitxer\n");
    }
    return 0;
}

和文件 yodatest.txt

may the force be with you.
may the force be with you every day of your life.
avui fa un dia molt bonic.
hola.
si entrenes molt i ets perseverant arribaras lluny i tindras molts poders.
un dia em comprare una moto i anire volant fins a l'estrella de la mort.
no hi ha dia que no m'agradi mirar les estrelles de nit.

Windows 下的 Ubuntu wsl 中编译和执行此代码时,我没有收到任何错误。但是,当我编译代码并在 MacOs 中执行它时,我得到输出:

[1] 60023 trace trap ./main.exe

有时正确执行,有时不正确,但我尝试在Ubuntu下执行了几次,我总是得到正确的答案。 此外,评论 mescla_string_taula 似乎有效,但同样,由于错误的性质,我无法确定。基本上,该函数只是打乱矩阵。 关于为什么会发生这种情况的任何线索?

一个正确的例子是:

Introdueix nom del fitxer: yodatest.txt
Introdueix velocitat de parla: 1
Introdueix el coeficient de yodificacio: 1
may the...force you...with be.
with the...force be...may of...every day...you your...life.
avui fa...un bonic...molt dia.
hola.
si molts...molt i...ets poders...arribaras lluny...i perseverant...entrenes tindras.
volant anire...em comprare...una fins...i dia...un moto...a l'estrella...de la...mort.
no de...ha dia...que nit...m'agradi mirar...les no...hi estrelles.
De processar...ha acabat, el fitxer

我将首先展示我是如何使用调试器来查找问题的,因为这项技术并不像它应该的那样广为人知。

首先,我将提供的源代码复制并粘贴到文件 yoda.c 中,并将提供的测试输入复制粘贴到文件 yodatest.txt 中。 (对于任何阅读本文的旁观者来说:这就是为什么我们总是要求在问题中以文本形式提供源代码和测试数据,而不是以图像或任何形式提供。从我的网站上复制和粘贴东西只需几秒钟浏览器,进入我的 C 编译环境,然后我关闭 运行ning。)

我编译了yoda.c和运行,得到了一个非常相似的错误:

Abort trap: 6

这很有前途。 (有时很难或不可能重现错误,这使得诊断难度增加了几个数量级。)

接下来我编译调试:

cc -g yoda.c

和运行调试器下的程序:

$ lldb a.out
(lldb) target create "a.out"
Current executable set to 'a.out' (x86_64).
(lldb) run

键入“运行”运行是程序。但是现在,当它崩溃时,我从调试器中得到了更多的输出:

2022-01-18 19:21:07.871139-0500 a.out[34597:99984533] detected source and destination buffer overlap
Process 34597 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
    frame #0: 0x00007fff57022e3e libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill:
->  0x7fff57022e3e <+10>: jae    0x7fff57022e48            ; <+20>
    0x7fff57022e40 <+12>: movq   %rax, %rdi
    0x7fff57022e43 <+15>: jmp    0x7fff5701a0b8            ; cerror_nocancel
    0x7fff57022e48 <+20>: retq   
Target 0: (a.out) stopped.

我可以输入“bt”来获取堆栈回溯:

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
  * frame #0: 0x00007fff57022e3e libsystem_kernel.dylib`__pthread_kill + 10
    frame #1: 0x00007fff57161150 libsystem_pthread.dylib`pthread_kill + 333
    frame #2: 0x00007fff56f7f312 libsystem_c.dylib`abort + 127
    frame #3: 0x00007fff56f7f485 libsystem_c.dylib`abort_report_np + 177
    frame #4: 0x00007fff56fa3c2d libsystem_c.dylib`__chk_fail + 48
    frame #5: 0x00007fff56fa3c3d libsystem_c.dylib`__chk_fail_overlap + 16
    frame #6: 0x00007fff56fa3c6e libsystem_c.dylib`__chk_overlap + 49
    frame #7: 0x00007fff56fa3e39 libsystem_c.dylib`__strcpy_chk + 64
    frame #8: 0x000000010000084e a.out`mescla_string_taula(taula=0x00007ffeefbff550, n_filas=12, coeficient=1) at yoda.c:44
    frame #9: 0x0000000100000907 a.out`alenteix_i_mescla_frase(frase="si entrenes molt i ets perseverant arribaras lluny i tindras molts poders.\n", frase_lenta="hola.", velocitat=1, coeficient=1) at yoda.c:56
    frame #10: 0x0000000100000dd2 a.out`main at yoda.c:117
    frame #11: 0x00007fff56ed3115 libdyld.dylib`start + 1

这告诉我对 strcpy 的违规调用是 yoda.c 的第 44 行。 (这很方便,因为程序中有四次调用strcpy,否则我可能不知道是哪一次导致了问题。)

显然是行

strcpy(taula[ind1], taula[ind2]);

in mescla_string_taula() 可以在自身之上复制一个字符串。 strcpy 未定义为在这种情况下正常运行。

定义为在重叠情况下正常工作的一个函数是memmove。我用

替换了有问题的行
memmove(taula[ind1], taula[ind2], strlen(taula[ind2])+1);

现在程序似乎可以运行了。

请注意,我没有更深入地分析问题,也没有检查其他问题,更不用说证明程序对所有输入都是正确的了。 (在评论中,Ruud Helderman 发现了其他一些可疑代码。)但在我的机器上,修改后的程序现在确实为那个测试用例生成了所需的输出。