从 OS X 中的 main 返回但不在 linux 上时中止陷阱 6

Posted

技术标签:

【中文标题】从 OS X 中的 main 返回但不在 linux 上时中止陷阱 6【英文标题】:Abort trap 6 when returning from main in OS X but NOT on linux 【发布时间】:2016-11-05 21:16:10 【问题描述】:

我有一个程序目前似乎在 Linux (Ubuntu 14.04) 上运行良好,但在 OS X (10.11.6) 上运行时出现中止陷阱 6。我附上了我的代码,但我怀疑问题出在实际上与特定代码无关。这段代码是针对一个班级项目的,我实际上并没有试图破解密码或任何东西。

这是代码,我相信所有重要的事情都发生在 main.js 中。

#include <openssl/aes.h>   
#include <openssl/evp.h>
#include <openssl/conf.h>
#include <openssl/err.h>


#define KEY_BYTES KEY_LENGTH/8
#define KEY_LENGTH 128


unsigned char*  h(unsigned char* p, unsigned char* hp);
void handleErrors(void);
int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,
            unsigned char *iv, unsigned char *ciphertext);


//assumes we've already padded with zeros
unsigned char*  h(unsigned char* p, unsigned char *hp)
    encrypt((unsigned char*)"0000000000000000", KEY_BYTES, p , (unsigned char*)"0000000000000000", hp);
    return hp; 


void handleErrors(void)

    printf("panic!!\n");
    //ERR_print_errors_fp(stderr); //sg: throw a real error you fool!
    abort();


//sg: stolen from the evp man page
int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,
            unsigned char *iv, unsigned char *ciphertext)

    EVP_CIPHER_CTX *ctx;

    int len;

    int ciphertext_len;

    /* Create and initialise the context */
    if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();

    /* Initialise the encryption operation. IMPORTANT - ensure you use a key
     * and IV size appropriate for your cipher
     * In this example we are using 256 bit AES (i.e. a 256 bit key). The
     * IV size for *most* modes is the same as the block size. For AES this
     * is 128 bits */
    if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
        handleErrors();

    /* Provide the message to be encrypted, and obtain the encrypted output.
     * EVP_EncryptUpdate can be called multiple times if necessary
     */
    if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
        handleErrors();
    ciphertext_len = len;

    /* Finalise the encryption. Further ciphertext bytes may be written at
     * this stage.
     */
    if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
    ciphertext_len += len;

    /* Clean up */
    EVP_CIPHER_CTX_free(ctx);

    return ciphertext_len;



int main()

    /* Initialise the library */
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    OPENSSL_config(NULL);

    EVP_CIPHER_CTX *ctx;
    unsigned char hp[KEY_BYTES];

    /* Create and initialise the context */
    if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();

    h((unsigned char*) "1111111111111111", hp);

    for(int i = 0; i < KEY_BYTES; i++)
        printf("h(%i) = %x\n", i, hp[i]);
    

    return 0;

在 linux 上运行时,我得到以下信息(这是我所期望的)

h(0) = 10
h(1) = df
h(2) = c1
h(3) = b5
h(4) = f6
h(5) = 6c
h(6) = fd
h(7) = 6a
h(8) = 1d
h(9) = c4
h(10) = 6d
h(11) = 66
h(12) = 90
h(13) = 7b
h(14) = ee
h(15) = b1

但是,当我在 OS X 上运行时,我得到以下信息:

h(0) = 10
h(1) = df
h(2) = c1
h(3) = b5
h(4) = f6
h(5) = 6c
h(6) = fd
h(7) = 6a
h(8) = 1d
h(9) = c4
h(10) = 6d
h(11) = 66
h(12) = 90
h(13) = 7b
h(14) = ee
h(15) = b1
Abort trap: 6

当我将它弹出到 gdb 中时,我得到以下信息

(gdb) r
Starting program: /Users/sgillen/Code/457/proj3/a.out 
h(0) = 10
h(1) = df
h(2) = c1
h(3) = b5
h(4) = f6
h(5) = 6c
h(6) = fd
h(7) = 6a
h(8) = 1d
h(9) = c4
h(10) = 6d
h(11) = 66
h(12) = 90
h(13) = 7b
h(14) = ee
h(15) = b1

Program received signal SIGABRT, Aborted.
0x00007fff93150f06 in __pthread_kill () from /usr/lib/system/libsystem_kernel.dylib
(gdb) where
#0  0x00007fff93150f06 in __pthread_kill () from /usr/lib/system/libsystem_kernel.dylib
#1  0x00007fff97b374ec in pthread_kill () from /usr/lib/system/libsystem_pthread.dylib
#2  0x00007fff9ba8077f in __abort () from /usr/lib/system/libsystem_c.dylib
#3  0x00007fff9ba8105e in __stack_chk_fail () from /usr/lib/system/libsystem_c.dylib
#4  0x0000000100000ea9 in main () at gen_table.cpp:90

不确定如何在堆栈溢出时处理行号,但 gen_table.cpp 的第 90 行是 main 中的最后一个返回 0。

如果相关,我会使用以下代码编译我的代码。

clang -Wall -std=c++11 gen_table.cpp  -I/usr/local/opt/openssl/include/ -lcrypto -lssl -g

任何帮助将不胜感激谢谢!

【问题讨论】:

程序中的某些东西可能会导致未定义的行为。使用valgrind 之类的工具来检测内存损坏。 您的 KEY_BYTESKEY_LENGTH 定义顺序错误。 感谢巴尔默的评论。我已经在程序上运行了 valgrind 并且没有收到任何错误或警告(如果您愿意,我可以向您显示确切的输出)。在这种情况下顺序不重要吗?我的理解是,预编译器将运行并用 KEY_LENGTH/8 替换 KEY_BYTES,然后运行并用 128/8 替换 KEY_LENGTH/8。如果我在那里错了,或者如果以相反的顺序更有效,请纠正我。另外,如果有更好的方法可以在堆栈溢出时回复 cmets,请随时告诉我。 你是对的,它在调用宏时扩展引用的宏,而不是在定义时。 【参考方案1】:

我找到了我的问题的答案,我想我会为遇到同样问题的其他人发布答案。问题是我正在覆盖我自己的堆栈。我使用的加密函数实际上是向 hp 写入 32 个字节(这是一个 16 字节的 unsigned char 存在于堆栈中)。所以我会销毁我自己的堆栈,但不会写入任何不属于我的进程的内存。这导致没有段错误,但是当程序尝试返回时,最终出现了问题。杀死我的确切事情会根据我编译代码的方式而改变。

我真的很惊讶 valgrind 没有抓住这一点。而且我仍然不知道为什么在使用 clang 编译时它似乎在 linux 上运行良好(使用 g++ 编译时出现堆栈粉碎检测错误)。

编辑: 明确的解决方案是修复我的加密实现,使其仅写入 16 个字节。我只是通过注释 EVP_EncryptFinal_ex 调用来做到这一点。

【讨论】:

解决了什么问题?

以上是关于从 OS X 中的 main 返回但不在 linux 上时中止陷阱 6的主要内容,如果未能解决你的问题,请参考以下文章

pathForResource 在 Mac OS X 控制台应用程序中返回 nil — Swift

如何从 Nim 的主块返回退出代码?

如何从 Jar 运行一个不在其 Manifest 文件中的 Main-Class 的类

从 main(从 dev)恢复的两个提交,然后在再次将 dev 合并到 main 之后,这些恢复的更改不在 main

从 OS X 迁移到 linux Red-Hat 时出错

无法从 OS X 访问 iTunes 音乐商店