--- Begin Message ---
- To: Debian Bug Tracking System <submit@bugs.debian.org>
- Subject: libffi-dev: ffi_call segfault: read beyond the heap, allocated for return value
- From: Ygrex <bi@hl.ru>
- Date: Thu, 19 Aug 2010 12:48:41 +0400
- Message-id: <20100819084841.4093.71749.reportbug@admin.hl.ru>
Package: libffi-dev
Version: 3.0.9-2
Severity: important
The test C-code is attached:
gcc-4.4 -lffi -lunistring -o test test.c
It can be compiled without libunistring as well (see notes, please):
1. comment out rows #7 and #8
2. uncomment #9
3. gcc-4.4 -lffi -o test test.c
What the code does:
1. creates a new ffi_type to emulate size_t;
2. allocates array of types of function arguments: (char *) and (size_t);
3. prepare CIF to call the size_t function with two arguments;
4. allocates array of pointers to two arguments: char *b and size_t a;
5. runs ffi_call and segfaults;
The function can be called directly without ffi_call: comment #68 and uncomment the next statement. In this case the code will not cause segfault.
I use valgrind to see internals:
valgrind --track-origins=yes -q ./test
In the case with ffi_call, valgrind shows:
--------------------vvvvvvvvvvvvvvvvvvvvvv
just before...
==4153== Conditional jump or move depends on uninitialised value(s)
==4153== at 0x4084827: u8_mbtoucr (in /usr/lib/libunistring.so.0.1.2)
==4153== by 0x40842E8: u8_mbsnlen (in /usr/lib/libunistring.so.0.1.2)
==4153== by 0x404154E: ffi_call_SYSV (in /usr/lib/libffi.so.5.0.10)
==4153== by 0x404138D: ffi_call (in /usr/lib/libffi.so.5.0.10)
==4153== by 0x804890C: main (in /home/ygrex/devel/dlffi/test)
==4153== Uninitialised value was created by a heap allocation
==4153== at 0x4023F50: malloc (vg_replace_malloc.c:236)
==4153== by 0x8048852: main (in /home/ygrex/devel/dlffi/test)
==4153==
==4153== Invalid read of size 1
==4153== at 0x4084822: u8_mbtoucr (in /usr/lib/libunistring.so.0.1.2)
==4153== by 0x40842E8: u8_mbsnlen (in /usr/lib/libunistring.so.0.1.2)
==4153== by 0x404154E: ffi_call_SYSV (in /usr/lib/libffi.so.5.0.10)
==4153== by 0x404138D: ffi_call (in /usr/lib/libffi.so.5.0.10)
==4153== by 0x804890C: main (in /home/ygrex/devel/dlffi/test)
==4153== Address 0x429a0ec is 0 bytes after a block of size 4 alloc'd
==4153== at 0x4023F50: malloc (vg_replace_malloc.c:236)
==4153== by 0x8048852: main (in /home/ygrex/devel/dlffi/test)
==4153==
==4153==
==4153== Process terminating with default action of signal 11 (SIGSEGV)
==4153== Access not within mapped region at address 0x469A000
==4153== at 0x4084822: u8_mbtoucr (in /usr/lib/libunistring.so.0.1.2)
==4153== by 0x40842E8: u8_mbsnlen (in /usr/lib/libunistring.so.0.1.2)
==4153== by 0x404154E: ffi_call_SYSV (in /usr/lib/libffi.so.5.0.10)
==4153== by 0x404138D: ffi_call (in /usr/lib/libffi.so.5.0.10)
==4153== by 0x804890C: main (in /home/ygrex/devel/dlffi/test)
==4153== If you believe this happened as a result of a stack
==4153== overflow in your program's main thread (unlikely but
==4153== possible), you can try to increase the size of the
==4153== main thread stack using the --main-stacksize= flag.
==4153== The main thread stack size used in this run was 8388608.
Segmentation fault
--------------------^^^^^^^^^^^^^^^^^^^^^^
the message "0 bytes after a block of size 4 alloc'd" is about #52:
void *ret = malloc(T_size_t->size);
this pointer is used as a return value allocation then.
Some notes on libunistring vs default strings: when I use strnlen instead of u8_mbsnlen and modify the 52-nd line:
void *ret = calloc(T_size_t->size, 1);
valgrind is happy and no segmentation fault occurs (but it still here with malloc);
-- System Information:
Debian Release: squeeze/sid
APT prefers unstable
APT policy: (500, 'unstable'), (500, 'testing'), (500, 'stable'), (1, 'experimental')
Architecture: i386 (i686)
Kernel: Linux 2.6.33.1-ygrex (SMP w/2 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/bash
Versions of packages libffi-dev depends on:
ii dpkg 1.15.8.4 Debian package management system
ii install-info 4.13a.dfsg.1-5 Manage installed documentation in
ii libffi5 3.0.9-2 Foreign Function Interface library
libffi-dev recommends no packages.
libffi-dev suggests no packages.
-- no debconf information
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <ffi.h>
#include <unistr.h>
size_t (* sample)(const uint8_t *, size_t) = u8_mbsnlen;
//size_t (* sample)(const char *, size_t) = strnlen;
ffi_type *type_init(size_t size)
{
ffi_type *new = malloc(sizeof(ffi_type));
if (!new) return NULL;
new->size = new->alignment = 0;
new->type = FFI_TYPE_STRUCT;
new->elements = calloc(size + 1, sizeof(ffi_type *));
if (!new->elements) {
free(new);
return NULL;
}
size_t i;
for (i = 0; i < size; i++) new->elements[i] = &ffi_type_uchar;
new->elements[size] = NULL;
return new;
}
int main()
{
ffi_type *T_size_t = type_init(sizeof(size_t));
if (! T_size_t) return -1;
ffi_type **types = calloc( 2, sizeof(ffi_type *));
if (! types) {
free(T_size_t->elements);
free(T_size_t);
return -2;
}
types[0] = &ffi_type_pointer;
types[1] = T_size_t;
ffi_cif cif;
ffi_status stat = ffi_prep_cif(
&cif,
FFI_DEFAULT_ABI,
2,
T_size_t,
types
);
void *ret = malloc(T_size_t->size);
void **argv = calloc( 2, sizeof(void *));
if ((stat != FFI_OK) || !ret || !argv) {
free(types);
free(T_size_t->elements);
free(T_size_t);
return -3;
}
size_t a = 10;
const char *b = "текст";
argv[1] = &a;
argv[0] = &b;
puts("just before...");
ffi_call(&cif, (void *)sample, ret, argv);
/*
*(size_t *)ret = (size_t)sample(
*(const uint8_t **)(argv[0]),
*(size_t *)(argv[1])
);
*/
puts("right after!");
printf("ret value: %zu\n", *(size_t *)ret);
free(argv);
free(ret);
free(types);
free(T_size_t->elements);
free(T_size_t);
return 0;
}
--- End Message ---