【问题标题】:Segmentation fault when running my own C program with crontab on Linux在 Linux 上使用 crontab 运行我自己的 C 程序时出现分段错误
【发布时间】:2021-06-03 11:06:25
【问题描述】:

uname -a:

Linux deepin 5.4.70-amd64-desktop #1 SMP Wed Oct 14 15:24:23 CST 2020 x86_64 GNU/Linux

我正在编写一个使用目录和套接字的 C 程序。当我通过命令行运行它时,它可以按预期工作,但是当我通过 cron 运行时,它会遇到分段错误。

这是我的程序的入口点(以及相关部分,我认为)。

#include "../include/persistence.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <libgen.h>
        
#include "../include/sockets.h"
#include "../include/footprint.h"
#include "../include/commands.h"

int
main(int argc, char *argv[]) {
    char    destination_path[2048] = { 0 };
    char    *exec_filename;
    char    *safe_exec_filename;
    char    *username;

    (void)argc;  // to prevent compiler warning about unused argc
    username = getenv("USER");
    safe_exec_filename = strdup(argv[0]);  // for safe use of basename()
    exec_filename = basename(safe_exec_filename);
    sprintf(destination_path, "/home/%s/.local/bin/", username);
    if (!file_exists(destination_path)) {
        if (create_dir(destination_path)) {
            strcat(destination_path, exec_filename);
            hide_file(argv[0], destination_path);
        }
    }
    if ((strlen(exec_filename) + strlen(destination_path)) > 2048) {
        printf("BUFFER TOO SMALL\n");
        return 1;
    }
    strcat(destination_path, exec_filename);
    printf("%s\n", destination_path);
    if (!file_exists(destination_path)) {
        hide_file(argv[0], destination_path);
    }
    if (safe_exec_filename != NULL) {
        free(safe_exec_filename);
    }
    persistence(destination_path);
    while (1) {
        int16_t server_socket = connect_to_server();

        start_communication(server_socket);
    }
    return 0;
}

通过命令行从任何(相对/绝对)路径执行二进制文件都可以正常工作,但使用 cron,“什么都没有”发生。分析/var/log/syslog时,显示如下:

Mar  4 15:27:01 deepin CRON[14713]: (user) CMD (/home/user/.local/bin/myprogram)
Mar  4 15:27:01 deepin kernel: [ 5934.175052] myprogram[22332]: segfault at 0 ip 00007f4fb5cea327 sp 00007ffd74362328 error 4 in libc-2.28.so[7f4fb5c6f000+148000]
Mar  4 15:27:01 deepin kernel: [ 5934.175060] Code: 0f 7f 27 f3 0f 7f 6f 10 f3 0f 7f 77 20 48 83 c6 30 48 83 c7 30 4c 8d 1d 47 f7 0d 00 49 63 0c 93 49 8d 0c 0b ff e1 66 0f ef c0 <f3> 0f 6f 0e f3 0f 6f 56 10 66 0f 74 c1 66 0f d7 d0 48 85 d2 75 6b

还有我的 crontab -l 输出:

* * * * * /home/user/.local/bin/myprogram

PS:我尝试了@reboot,但将 * * * * * 更改为“调试”。在使用@reboot 并检查日志时,也会出现分段错误。检查 /var/mail/user,它显示:

Content-Transfer-Encoding: 8bit
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <HOME=/home/user>
X-Cron-Env: <PATH=/usr/bin:/bin>
X-Cron-Env: <LOGNAME=user>
Message-Id: <20210304184201.5A18124898@deepin>
Date: Thu,  4 Mar 2021 15:27:02 -0300 (-03)

Segmentation fault

编辑1:我已经尝试(在主函数的顶部)放置一个创建文件的函数,写入文件并保存。这个函数永远不会执行。我创建了另一个程序,它只有一个写入文件并保存的主函数。将二进制文件放在 myprogram 的同一个文件夹中,并为其创建一个 cron 并正常执行(我可以看到生成的文件,并且日志上没有显示段错误或其他错误)。

【问题讨论】:

  • 尝试在 valgrind 下运行您的代码。如果你的内存管理不善,它会告诉你在哪里。
  • 通过命令行使用--leak-check=full 运行valgrind 显示没有错误并且程序进入recv(如预期的那样)。我不知道如何使用 crontrab 运行 valgrind。
  • 解决了这个answer。感谢@Nate Eldredge。
  • 如何避免code has no purpose 警告(关于第一个语句argc; 的存在)???

标签: c linux cron segmentation-fault


【解决方案1】:

当您从 shell 运行程序时,它设置了许多环境变量,从 cron 运行时可能存在也可能不存在。 USER 就是其中之一。你应该检查你系统的文档,但是mine 有:

cron(8) 守护程序会自动设置几个环境变量。 SHELL 设置为 /bin/sh,LOGNAME 和 HOME 设置自 crontab 所有者的 /etc/passwd 行。 HOME 和 SHELL 可能会被 crontab 中的设置覆盖; LOGNAME 可能不会。

(另一个注意事项:在 BSD 系统上,LOGNAME 变量有时称为 USER...在这些系统上,也会设置 USER。)

所以USER 可能没有定义。如果不是,则 getenv("USER") 将返回 NULL,但您不会对此进行测试,因此当您将 username 传递给 sprintf 时,username 将是 NULL 和 segfault。

您也许可以改用LOGNAME,或者跳过环境变量并找到带有getpwuid(getuid()) 之类的用户名。

在任何情况下,一般来说,进行更多的错误检查是一个好主意,以防止出现更多此类错误。

【讨论】:

  • 使用 getpwuid(getuid()) 而不是使用环境变量,现在一切正常,谢谢!已接受答案。
【解决方案2】:

我认为问题可能来自各种问题:

为什么需要循环连接服务器?

while (1) {
        int16_t server_socket = connect_to_server();

        start_communication(server_socket);
    }
    return 0;

应该是:

    int16_t server_socket = connect_to_server();
    while (1) {
        start_communication(server_socket);
    }
    return 0;

而不是:

free(safe_exec_filename);

使用:

if (safe_exec_filename == NULL) {
    dprintf(2, "Safe exec filename is NULL");
    return 1;
}
free(safe_exec_filename);

同时使用 cron 和你的 c 程序打印 safe_exec_filename 看看是否有区别,你的段错误的程序集如下:


你的 char 数组变得太小了:

0:  0f 7f 27                movq   QWORD PTR [edi],mm4
3:  f3 0f 7f 6f 10          movdqu XMMWORD PTR [edi+0x10],xmm5
8:  f3 0f 7f 77 20          movdqu XMMWORD PTR [edi+0x20],xmm6
d:  48                      dec    eax
e:  83 c6 30                add    esi,0x30
11: 48                      dec    eax
12: 83 c7 30                add    edi,0x30
15: 4c                      dec    esp
16: 8d 1d 47 f7 0d 00       lea    ebx,ds:0xdf747
1c: 49                      dec    ecx
1d: 63 0c 93                arpl   WORD PTR [ebx+edx*4],cx
20: 49                      dec    ecx
21: 8d 0c 0b                lea    ecx,[ebx+ecx*1]
24: ff e1                   jmp    ecx
26: 66 0f ef c0             pxor   xmm0,xmm0
2a: f3 0f 6f 0e             movdqu xmm1,XMMWORD PTR [esi]
2e: f3 0f 6f 56 10          movdqu xmm2,XMMWORD PTR [esi+0x10]
33: 66 0f 74 c1             pcmpeqb xmm0,xmm1
37: 66 0f d7 d0             pmovmskb edx,xmm0
3b: 48                      dec    eax
3c: 85 d2                   test   edx,edx
3e: 75 6b                   jne    0xab

所以我主要担心的是它可能与目标路径的缓冲区变得太小有关,因为 cron 使用相对路径而不是绝对路径:

strcat(destination_path, exec_filename);

尝试改变缓冲区大小:

#define SIZE 2048
char    destination_path[SIZE] = { 0 };

并添加支票:

if ((strlen(exec_filename) + strlen(destination_path)) > SIZE)
{ error handling}
strcat(destination_path, exec_filename);

【讨论】:

  • 我需要循环连接,因为如果通信发生错误,客户端会一直尝试连接到服务器。对数组大小进行了更改并添加了一些打印。在命令行上一切正常,但使用 cron,seg 错误不断发生。我在 crontab 上使用: * * * * * /home/user/.local/bin/myprogram &> /home/user/Desktop/output.txt 来捕获标准输出和错误,但它没有写入文件。跨度>
猜你喜欢
  • 2013-04-24
  • 2013-11-30
  • 2013-09-13
  • 1970-01-01
  • 2021-07-03
  • 2021-07-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多