Subject: | When built with latest clang, tests fail with "Abort trap: 6" due to detecting "buffer overflow" related to Tcl_HashEntry |
Ok so this is icky. I just tried to build a perl from scratch and then perl-Tk (as I have done many times before) with the latest clang from Apple's Xcode (but the latest llvm/clang distro will show the same behaviour).
make test was failing nearly immediately with "Abort trap: 6". When built with -g and run under lldb, I got:
(lldb) r
r
Process 90756 launched: '/Users/markaufflick/src/_Pumptheory/EFC/hnav1/gdt-app/perl/bin/perl' (x86_64)
2017-05-12 20:59:22.907474 perl[90756:2863014] detected buffer overflow
Process 90756 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
frame #0: 0x00007fffb7bdedd6 libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill:
-> 0x7fffb7bdedd6 <+10>: jae 0x7fffb7bdede0 ; <+20>
0x7fffb7bdedd8 <+12>: movq %rax, %rdi
0x7fffb7bdeddb <+15>: jmp 0x7fffb7bd7cdf ; cerror_nocancel
0x7fffb7bdede0 <+20>: retq
(lldb) bt
bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
* frame #0: 0x00007fffb7bdedd6 libsystem_kernel.dylib`__pthread_kill + 10
frame #1: 0x00007fffb7cca787 libsystem_pthread.dylib`pthread_kill + 90
frame #2: 0x00007fffb7b44420 libsystem_c.dylib`abort + 129
frame #3: 0x00007fffb7b44592 libsystem_c.dylib`abort_report_np + 181
frame #4: 0x00007fffb7b6af28 libsystem_c.dylib`__chk_fail + 48
frame #5: 0x00007fffb7b6aef8 libsystem_c.dylib`__chk_fail_overflow + 16
frame #6: 0x00007fffb7b6b145 libsystem_c.dylib`__strcpy_chk + 83
frame #7: 0x0000000100723467 Tk.bundle`AllocStringEntry + 87
frame #8: 0x000000010072375f Tk.bundle`Tcl_CreateHashEntry + 287
frame #9: 0x00000001007313ec Tk.bundle`TkBindInit + 92
frame #10: 0x0000000100790e83 Tk.bundle`TkCreateMainWindow + 195
frame #11: 0x000000010074a157 Tk.bundle`CreateFrame + 807
frame #12: 0x00000001007124be Tk.bundle`XS_Tk__MainWindow_Create(cv=<unavailable>) at tkGlue.c:2378 [opt]
frame #13: 0x000000010009d229 perl`Perl_pp_entersub + 2073
frame #14: 0x0000000100095fc3 perl`Perl_runops_standard + 19
frame #15: 0x000000010002247e perl`perl_run + 878
frame #16: 0x000000010000128f perl`main + 159
frame #17: 0x00007fffb7ab0255 libdyld.dylib`start + 1
(lldb)
The problem is that whatever clang's runtime library is doing to detect buffer overflows really doesn't like how Tcl_HashEntry has a union with a char[4] array that is "abused" to be as long a string as we like. The AllocStringEntry symbol causing the problem above is:
static Tcl_HashEntry *
AllocStringEntry(tablePtr, keyPtr)
Tcl_HashTable *tablePtr; /* Hash table. */
VOID *keyPtr; /* Key to store in the hash table entry. */
{
CONST char *string = (CONST char *) keyPtr;
Tcl_HashEntry *hPtr;
unsigned int size;
size = sizeof(Tcl_HashEntry) + strlen(string) + 1 - sizeof(hPtr->key);
if (size < sizeof(Tcl_HashEntry))
size = sizeof(Tcl_HashEntry);
hPtr = (Tcl_HashEntry *) ckalloc(size);
strcpy(hPtr->key.string, string);
return hPtr;
}
But I can reproduce exactly the same crash with this minimal C:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct Tcl_HashEntry {
union { /* Key has one of these forms: */
char *oneWordValue; /* One-word value for key. */
int words[1]; /* Multiple integer words for key.
* The actual size will be as large
* as necessary for this table's
* keys. */
char string[4]; /* String for key. The actual size
* will be as large as needed to hold
* the key. */
} key; /* MUST BE LAST FIELD IN RECORD!! */
} Tcl_HashEntry;
int main (void) {
char * string = "fooo";
Tcl_HashEntry *hPtr;
size_t size = sizeof(Tcl_HashEntry) + strlen(string) + 1 - sizeof(hPtr->key);
hPtr = (Tcl_HashEntry *)calloc (1, size);
strcpy (hPtr->key.string, string);
printf ("foo: %s\n", hPtr->key.string);
return 0;
}
It works fine if string is "foo" (which with null is four chars), but crashes with Abort trap: 6 for "fooo"...
Not quite sure what to do here - changing Tcl_HashEntry isn't exactly trivial. I'll investigate if these runtime checks can be turned off (although that wouldn't be ideal - perhaps they can be turned off just for some structures...)