Segfaulting Integer Overflow in g_option_group_add_entries
0. which version of GLib are you using?
2.58.3 (2.58.3-2+deb10u2, latest stable libglib2.0-0 package for Debian Buster)
1. which operating system are you using?
Debian Buster
2. the necessary steps to reproduce the issue
At the time of writing, setup an up-to-date Debian Buster instance with the latest libglib2.0-0 and dev packages and then compile and run the example code below.
3. the expected outcome
glib
should gracefully detect the unsafe state of GOptionGroup
, which can come from buggy software importing glib
, and handle it without allowing an integer overflow to occur inside g_option_group_add_entries
and segfaulting on an out-of-bounds write.
4. a description of the behavior
The beginning of g_option_group_add_entries
is susceptible to integer overflow because it performs several arithmetic operations without bounds checks on parameters that could come from buggy software that imports glib
:
https://gitlab.gnome.org/GNOME/glib/-/blob/2.58.3/glib/goption.c#L2352-2367
void
g_option_group_add_entries (GOptionGroup *group,
const GOptionEntry *entries)
{
gint i, n_entries;
g_return_if_fail (entries != NULL);
for (n_entries = 0; entries[n_entries].long_name != NULL; n_entries++) ;
group->entries = g_renew (GOptionEntry, group->entries, group->n_entries + n_entries);
/* group->entries could be NULL in the trivial case where we add no
* entries to no entries */
if (n_entries != 0)
memcpy (group->entries + group->n_entries, entries, sizeof (GOptionEntry) * n_entries);
If group->n_entries + n_entries
causes an overflow, the subsequent memcpy
will cause an out-of-bounds write.
I do not think this bug is a security concern at this time because I do not know of a realistic scenario where group->n_entries
or n_entries
would become sufficiently large to trigger the bug. However, this should be patched to be on the safe side.
5. a small, self-contained example exhibiting the behavior
Note: I intentionally expose the elements of GOptionGroup
to keep the example brief and concise. I realize that software importing glib
is not normally suppose to touch the elements of this struct directly. That said, accidents happen.
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#include <glib/goption.h>
/* gcc -g -Wall -o test -I/usr/include/glib-2.0 \
* -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -lglib-2.0 test.c
*/
// Normally private, expose it for the sake of making the demo easier to follow
struct _GOptionGroup
{
gchar *name;
gchar *description;
gchar *help_description;
gint ref_count;
GDestroyNotify destroy_notify;
gpointer user_data;
GTranslateFunc translate_func;
GDestroyNotify translate_notify;
gpointer translate_data;
GOptionEntry *entries;
gint n_entries;
GOptionParseFunc pre_parse_func;
GOptionParseFunc post_parse_func;
GOptionErrorFunc error_func;
};
int main(int argc, char **argv) {
struct _GOptionGroup group;
GOptionEntry *entries;
char *entry0_longname = "Foobar";
// Create an array of entries ending with a null entry
entries = (GOptionEntry *) calloc(2, sizeof(GOptionEntry));
entries[0].long_name = entry0_longname;
entries[1].long_name = NULL;
// Set group.n_entries 1 away from overflowing
group.n_entries = -1;
// group needs a valid non-null group.entries pointer, otherwise
// g_realloc will cause an abort
group.entries = (GOptionEntry *) calloc(1, sizeof(GOptionEntry));
// Trigger integer overflow (this will segfault)
g_option_group_add_entries((GOptionGroup *) &group, entries);
return 0;
}
If the issue includes a crash, you should also include:
0. the eventual warnings printed on the terminal
$ ./test
Segmentation fault
1. a backtrace, obtained with tools such as GDB or LLDB
$ gdb test
(gdb) r
Starting program: /test
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
__memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:240
240 ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S: No such file or directory.
(gdb) bt
#0 __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:240
#1 0x00007ffff7ef0128 in memcpy (__len=48, __src=0x55555555f080, __dest=<optimized out>) at /usr/include/x86_64-linux-gnu/bits/string_fortified.h:34
#2 g_option_group_add_entries (entries=<optimized out>, group=<optimized out>) at ../../../glib/goption.c:2367
#3 g_option_group_add_entries (group=0x7fffffffe3a0, entries=0x55555555f080) at ../../../glib/goption.c:2353
#4 0x00005555555551c2 in main (argc=1, argv=0x7fffffffe508) at test.c:52