summaryrefslogtreecommitdiff
path: root/gc.c
diff options
context:
space:
mode:
author卜部昌平 <shyouhei@ruby-lang.org>2019-10-08 16:07:31 +0900
committer卜部昌平 <shyouhei@ruby-lang.org>2019-10-09 12:12:28 +0900
commita14cc07f2ffc704b73ba4b96543e2f85c3ed1921 (patch)
tree5a1401287127827345cfe080ec98bb715ca2fab5 /gc.c
parent7e0ae1698d4db0baec858a46de8d1ae875360cf5 (diff)
avoid returning NULL from xrealloc
This changeset is to kill future possibility of bugs similar to CVE-2019-11932. The vulnerability occurs when reallocarray(3) (which is a variant of realloc(3) and roughly resembles our ruby_xmalloc2()) returns NULL. In our C API, ruby_xmalloc() never returns NULL to raise NoMemoryError instead. ruby_xfree() does not return NULL by definition. ruby_xrealloc() on the other hand, _did_ return NULL, _and_ also raised sometimes. It is very confusing. Let's not do that. x-series APIs shall raise on error and shall not return NULL.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/2540
Diffstat (limited to 'gc.c')
-rw-r--r--gc.c44
1 files changed, 42 insertions, 2 deletions
diff --git a/gc.c b/gc.c
index 2ff90942e6..9a33da0cf2 100644
--- a/gc.c
+++ b/gc.c
@@ -9927,8 +9927,41 @@ objspace_xrealloc(rb_objspace_t *objspace, void *ptr, size_t new_size, size_t ol
* see http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm
*/
if (new_size == 0) {
- objspace_xfree(objspace, ptr, old_size);
- return 0;
+ if ((mem = objspace_xmalloc0(objspace, 0)) != NULL) {
+ /*
+ * - OpenBSD's malloc(3) man page says that when 0 is passed, it
+ * returns a non-NULL pointer to an access-protected memory page.
+ * The returned pointer cannot be read / written at all, but
+ * still be a valid argument of free().
+ *
+ * https://man.openbsd.org/malloc.3
+ *
+ * - Linux's malloc(3) man page says that it _might_ perhaps return
+ * a non-NULL pointer when its argument is 0. That return value
+ * is safe (and is expected) to be passed to free().
+ *
+ * http://man7.org/linux/man-pages/man3/malloc.3.html
+ *
+ * - As I read the implementation jemalloc's malloc() returns fully
+ * normal 16 bytes memory region when its argument is 0.
+ *
+ * - As I read the implementation musl libc's malloc() returns
+ * fully normal 32 bytes memory region when its argument is 0.
+ *
+ * - Other malloc implementations can also return non-NULL.
+ */
+ objspace_xfree(objspace, ptr, old_size);
+ return mem;
+ }
+ else {
+ /*
+ * It is dangerous to return NULL here, because that could lead to
+ * RCE. Fallback to 1 byte instead of zero.
+ *
+ * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11932
+ */
+ new_size = 1;
+ }
}
#if CALC_EXACT_MALLOC_SIZE
@@ -10016,6 +10049,13 @@ rb_malloc_info_show_results(void)
static void
objspace_xfree(rb_objspace_t *objspace, void *ptr, size_t old_size)
{
+ if (!ptr) {
+ /*
+ * ISO/IEC 9899 says "If ptr is a null pointer, no action occurs" since
+ * its first version. We would better follow.
+ */
+ return;
+ }
#if CALC_EXACT_MALLOC_SIZE
struct malloc_obj_info *info = (struct malloc_obj_info *)ptr - 1;
ptr = info;