Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libbpf: Fix double-free when linker processes empty sections

Double-free error in bpf_linker__free() was reported by James Hilliard.
The error is caused by miss-use of realloc() in extend_sec().
The error occurs when two files with empty sections of the same name
are linked:
- when first file is processed:
- extend_sec() calls realloc(dst->raw_data, dst_align_sz)
with dst->raw_data == NULL and dst_align_sz == 0;
- dst->raw_data is set to a special pointer to a memory block of
size zero;
- when second file is processed:
- extend_sec() calls realloc(dst->raw_data, dst_align_sz)
with dst->raw_data == <special pointer> and dst_align_sz == 0;
- realloc() "frees" dst->raw_data special pointer and returns NULL;
- extend_sec() exits with -ENOMEM, and the old dst->raw_data value
is preserved (it is now invalid);
- eventually, bpf_linker__free() attempts to free dst->raw_data again.

This patch fixes the bug by avoiding -ENOMEM exit for dst_align_sz == 0.
The fix was suggested by Andrii Nakryiko <andrii.nakryiko@gmail.com>.

Reported-by: James Hilliard <james.hilliard1@gmail.com>
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Tested-by: James Hilliard <james.hilliard1@gmail.com>
Link: https://lore.kernel.org/bpf/CADvTj4o7ZWUikKwNTwFq0O_AaX+46t_+Ca9gvWMYdWdRtTGeHQ@mail.gmail.com/
Link: https://lore.kernel.org/bpf/20230328004738.381898-3-eddyz87@gmail.com

authored by

Eduard Zingerman and committed by
Andrii Nakryiko
d08ab82f 7283137a

+13 -1
+13 -1
tools/lib/bpf/linker.c
··· 1115 1115 1116 1116 if (src->shdr->sh_type != SHT_NOBITS) { 1117 1117 tmp = realloc(dst->raw_data, dst_final_sz); 1118 - if (!tmp) 1118 + /* If dst_align_sz == 0, realloc() behaves in a special way: 1119 + * 1. When dst->raw_data is NULL it returns: 1120 + * "either NULL or a pointer suitable to be passed to free()" [1]. 1121 + * 2. When dst->raw_data is not-NULL it frees dst->raw_data and returns NULL, 1122 + * thus invalidating any "pointer suitable to be passed to free()" obtained 1123 + * at step (1). 1124 + * 1125 + * The dst_align_sz > 0 check avoids error exit after (2), otherwise 1126 + * dst->raw_data would be freed again in bpf_linker__free(). 1127 + * 1128 + * [1] man 3 realloc 1129 + */ 1130 + if (!tmp && dst_align_sz > 0) 1119 1131 return -ENOMEM; 1120 1132 dst->raw_data = tmp; 1121 1133