mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-21 12:30:06 +00:00
gmon: fix memory corruption issues [BZ# 30101]
V2 of this patch fixes an issue in V1, where the state was changed to ON not OFF at end of _mcleanup. I hadn't noticed that (counterintuitively) ON=0 and OFF=3, hence zeroing the buffer turned it back on. So set the state to OFF after the memset. 1. Prevent double free, and reads from unallocated memory, when _mcleanup is (incorrectly) called two or more times in a row, without an intervening call to __monstartup; with this patch, the second and subsequent calls effectively become no-ops instead. While setting tos=NULL is minimal fix, safest action is to zero the whole gmonparam buffer. 2. Prevent memory leak when __monstartup is (incorrectly) called two or more times in a row, without an intervening call to _mcleanup; with this patch, the second and subsequent calls effectively become no-ops instead. 3. After _mcleanup, treat __moncontrol(1) as __moncontrol(0) instead. With zeroing of gmonparam buffer in _mcleanup, this stops the state incorrectly being changed to GMON_PROF_ON despite profiling actually being off. If we'd just done the minimal fix to _mcleanup of setting tos=NULL, there is risk of far worse memory corruption: kcount would point to deallocated memory, and the __profil syscall would make the kernel write profiling data into that memory, which could have since been reallocated to something unrelated. 4. Ensure __moncontrol(0) still turns off profiling even in error state. Otherwise, if mcount overflows and sets state to GMON_PROF_ERROR, when _mcleanup calls __moncontrol(0), the __profil syscall to disable profiling will not be invoked. _mcleanup will free the buffer, but the kernel will still be writing profiling data into it, potentially corrupted arbitrary memory. Also adds a test case for (1). Issues (2)-(4) are not feasible to test. Signed-off-by: Simon Kissane <skissane@gmail.com> Reviewed-by: DJ Delorie <dj@redhat.com>
This commit is contained in:
parent
31be941e43
commit
bde1218720
@ -1,4 +1,5 @@
|
|||||||
# Copyright (C) 1995-2023 Free Software Foundation, Inc.
|
# Copyright (C) 1995-2023 Free Software Foundation, Inc.
|
||||||
|
# Copyright The GNU Toolchain Authors.
|
||||||
# This file is part of the GNU C Library.
|
# This file is part of the GNU C Library.
|
||||||
|
|
||||||
# The GNU C Library is free software; you can redistribute it and/or
|
# The GNU C Library is free software; you can redistribute it and/or
|
||||||
@ -25,7 +26,7 @@ include ../Makeconfig
|
|||||||
headers := sys/gmon.h sys/gmon_out.h sys/profil.h
|
headers := sys/gmon.h sys/gmon_out.h sys/profil.h
|
||||||
routines := gmon mcount profil sprofil prof-freq
|
routines := gmon mcount profil sprofil prof-freq
|
||||||
|
|
||||||
tests = tst-sprofil tst-gmon tst-mcount-overflow
|
tests = tst-sprofil tst-gmon tst-mcount-overflow tst-mcleanup
|
||||||
ifeq ($(build-profile),yes)
|
ifeq ($(build-profile),yes)
|
||||||
tests += tst-profile-static
|
tests += tst-profile-static
|
||||||
tests-static += tst-profile-static
|
tests-static += tst-profile-static
|
||||||
@ -68,6 +69,14 @@ ifeq ($(run-built-tests),yes)
|
|||||||
tests-special += $(objpfx)tst-mcount-overflow-check.out
|
tests-special += $(objpfx)tst-mcount-overflow-check.out
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
CFLAGS-tst-mcleanup.c := -fno-omit-frame-pointer -pg
|
||||||
|
tst-mcleanup-no-pie = yes
|
||||||
|
CRT-tst-mcleanup := $(csu-objpfx)g$(start-installed-name)
|
||||||
|
tst-mcleanup-ENV := GMON_OUT_PREFIX=$(objpfx)tst-mcleanup.data
|
||||||
|
ifeq ($(run-built-tests),yes)
|
||||||
|
tests-special += $(objpfx)tst-mcleanup.out
|
||||||
|
endif
|
||||||
|
|
||||||
CFLAGS-tst-gmon-static.c := $(PIE-ccflag) -fno-omit-frame-pointer -pg
|
CFLAGS-tst-gmon-static.c := $(PIE-ccflag) -fno-omit-frame-pointer -pg
|
||||||
CRT-tst-gmon-static := $(csu-objpfx)g$(static-start-installed-name)
|
CRT-tst-gmon-static := $(csu-objpfx)g$(static-start-installed-name)
|
||||||
tst-gmon-static-no-pie = yes
|
tst-gmon-static-no-pie = yes
|
||||||
@ -123,6 +132,10 @@ $(objpfx)tst-mcount-overflow-check.out: tst-mcount-overflow-check.sh $(objpfx)ts
|
|||||||
$(SHELL) $< $(objpfx)tst-mcount-overflow > $@; \
|
$(SHELL) $< $(objpfx)tst-mcount-overflow > $@; \
|
||||||
$(evaluate-test)
|
$(evaluate-test)
|
||||||
|
|
||||||
|
$(objpfx)tst-mcleanup.out: clean-tst-mcleanup-data
|
||||||
|
clean-tst-mcleanup-data:
|
||||||
|
rm -f $(objpfx)tst-mcleanup.data.*
|
||||||
|
|
||||||
$(objpfx)tst-gmon-gprof.out: tst-gmon-gprof.sh $(objpfx)tst-gmon.out
|
$(objpfx)tst-gmon-gprof.out: tst-gmon-gprof.sh $(objpfx)tst-gmon.out
|
||||||
$(SHELL) $< $(GPROF) $(objpfx)tst-gmon $(objpfx)tst-gmon.data.* > $@; \
|
$(SHELL) $< $(GPROF) $(objpfx)tst-gmon $(objpfx)tst-gmon.data.* > $@; \
|
||||||
$(evaluate-test)
|
$(evaluate-test)
|
||||||
|
26
gmon/gmon.c
26
gmon/gmon.c
@ -102,11 +102,8 @@ __moncontrol (int mode)
|
|||||||
{
|
{
|
||||||
struct gmonparam *p = &_gmonparam;
|
struct gmonparam *p = &_gmonparam;
|
||||||
|
|
||||||
/* Don't change the state if we ran into an error. */
|
/* Treat start request as stop if error or gmon not initialized. */
|
||||||
if (p->state == GMON_PROF_ERROR)
|
if (mode && p->state != GMON_PROF_ERROR && p->tos != NULL)
|
||||||
return;
|
|
||||||
|
|
||||||
if (mode)
|
|
||||||
{
|
{
|
||||||
/* start */
|
/* start */
|
||||||
__profil((void *) p->kcount, p->kcountsize, p->lowpc, s_scale);
|
__profil((void *) p->kcount, p->kcountsize, p->lowpc, s_scale);
|
||||||
@ -116,7 +113,9 @@ __moncontrol (int mode)
|
|||||||
{
|
{
|
||||||
/* stop */
|
/* stop */
|
||||||
__profil(NULL, 0, 0, 0);
|
__profil(NULL, 0, 0, 0);
|
||||||
p->state = GMON_PROF_OFF;
|
/* Don't change the state if we ran into an error. */
|
||||||
|
if (p->state != GMON_PROF_ERROR)
|
||||||
|
p->state = GMON_PROF_OFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
libc_hidden_def (__moncontrol)
|
libc_hidden_def (__moncontrol)
|
||||||
@ -146,6 +145,14 @@ __monstartup (u_long lowpc, u_long highpc)
|
|||||||
maxarcs = MAXARCS;
|
maxarcs = MAXARCS;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are incorrectly called twice in a row (without an
|
||||||
|
* intervening call to _mcleanup), ignore the second call to
|
||||||
|
* prevent leaking memory.
|
||||||
|
*/
|
||||||
|
if (p->tos != NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* round lowpc and highpc to multiples of the density we're using
|
* round lowpc and highpc to multiples of the density we're using
|
||||||
* so the rest of the scaling (here and in gprof) stays in ints.
|
* so the rest of the scaling (here and in gprof) stays in ints.
|
||||||
@ -463,9 +470,14 @@ _mcleanup (void)
|
|||||||
{
|
{
|
||||||
__moncontrol (0);
|
__moncontrol (0);
|
||||||
|
|
||||||
if (_gmonparam.state != GMON_PROF_ERROR)
|
if (_gmonparam.state != GMON_PROF_ERROR && _gmonparam.tos != NULL)
|
||||||
write_gmon ();
|
write_gmon ();
|
||||||
|
|
||||||
/* free the memory. */
|
/* free the memory. */
|
||||||
free (_gmonparam.tos);
|
free (_gmonparam.tos);
|
||||||
|
|
||||||
|
/* reset buffer to initial state for safety */
|
||||||
|
memset(&_gmonparam, 0, sizeof _gmonparam);
|
||||||
|
/* somewhat confusingly, ON=0, OFF=3 */
|
||||||
|
_gmonparam.state = GMON_PROF_OFF;
|
||||||
}
|
}
|
||||||
|
31
gmon/tst-mcleanup.c
Normal file
31
gmon/tst-mcleanup.c
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/* Test program for repeated invocation of _mcleanup
|
||||||
|
Copyright The GNU Toolchain Authors.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with the GNU C Library; if not, see
|
||||||
|
<https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
/* Intentionally calls _mcleanup() twice: once manually, it will be
|
||||||
|
called again as an atexit handler. This is incorrect use of the API,
|
||||||
|
but the point of the test is to make sure we don't crash when the
|
||||||
|
API is misused in this way. */
|
||||||
|
|
||||||
|
#include <sys/gmon.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
_mcleanup();
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user