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