bin/60533: 4.9-STABLE libc locale support might contain buffer
overflows (or stack corruption, or double free() problem),
appearing on some configurations; fix and testcase attached
Michal Pasternak
dotz at irc.pl
Tue Dec 23 17:30:15 PST 2003
>Number: 60533
>Category: bin
>Synopsis: 4.9-STABLE libc locale support might contain buffer overflows (or stack corruption, or double free() problem), appearing on some configurations; fix and testcase attached
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Tue Dec 23 17:30:10 PST 2003
>Closed-Date:
>Last-Modified:
>Originator: Michal Pasternak
>Release: 4.9-STABLE
>Organization:
>Environment:
FreeBSD mainframe.w.lub.pl 4.9-STABLE FreeBSD 4.9-STABLE #0: Tue Dec 23 20:45:53 CET 2003 root at mainframe.w.lub.pl:/usr/obj/usr/src/sys/MP-UX i386
>Description:
*****************************************************************
Disclaimer: I might be totally wrong here, but this case is worth
looking at anyway. I have only partial knowledge about gcc, stack
frames, buffer overflows and such stuff.
*****************************************************************
After a few days of uptime, my fresh install of 4.9-RELEASE (from
mini-iso) started to behave in a strange way. Whenever I used LC_ALL,
LC_MESSAGES or LC_NUMERIC environment variables (they worked before!), standard FreeBSD programs from basesystem (like tcsh(1) or ls(1)) dumped
core while start-up, no matter which locale was used. My RAM is tested;
coredumps were repeatable; cvsup to -STABLE and buildworld/installworld
performed a few times (CFLAGS=-O) did *not* change the situation. As it
came out:
- programs coredumped at line 69 of src/libc/locale/ldpart.c, then,
after my patching, around line 125 (sorry, I don't have those
backtraces saved, but look at the code -- it just asks for a coredump,
not doing any NULL checking)
- errors appeared, whenever __part_load_locale was called from:
src/libc/locale files: lmessages.c, lnumeric.c and lmonetary.c.
src/libc/stdtime/timelocale.c
- errors appeared only with LC_ALL (or LC_* variables set to something
else, than "" or "C")
Below is the last gdb backtrace I took before squishing this bug:
Core was generated by `ls'.
Program terminated with signal 11, Segmentation fault.
#0 0x8073551 in __part_load_locale (name=0x80a7860 "en_US.ISO8859-1",
using_locale=0x80a9128, locale_buf=0x0,
category_filename=0x809f153 "LC_TIME", locale_buf_size_max=58,
locale_buf_size_min=58, dst_localebuf=0x80a9040)
at /usr/src/lib/libc/../libc/locale/ldpart.c:136
136 *locale_buf = lbuf;
(gdb) backtrace
#0 0x8073551 in __part_load_locale (name=0x80a7860 "en_US.ISO8859-1",
using_locale=0x80a9128, locale_buf=0x0,
category_filename=0x809f153 "LC_TIME", locale_buf_size_max=58,
locale_buf_size_min=58, dst_localebuf=0x80a9040)
at /usr/src/lib/libc/../libc/locale/ldpart.c:136
#1 0x8061d1b in __time_load_locale (name=0x80a7860 "en_US.ISO8859-1")
at /usr/src/lib/libc/../libc/stdtime/timelocal.c:113
#2 0x805cff4 in loadlocale (category=5)
at /usr/src/lib/libc/../libc/locale/setlocale.c:317
#3 0x805cc5d in setlocale (category=0, locale=0x80908e9 "")
at /usr/src/lib/libc/../libc/locale/setlocale.c:205
#4 0x804839c in main (argc=1, argv=0xbfbffb8c) at /usr/src/bin/ls/ls.c:145
The question is: did the bug occur because those programs were
static-linked? No idea. My pkgsrc-compiled (www.pkgsrc.org)
pkgsrc/editors/jed worked without problems (but hey, it didn't use
any LOCALE settings, I suppose). But -- on the other hand --
pkgsrc/databases/postgresql did coredumped.
The just error started to appear and there was no way to stop this. I
know, sounds silly and unbeliveable. But I have my RAM tested, I have
backtrace, hah -- I even have a patch, which fixed it. Please read on.
>How-To-Repeat:
This is the best part: I totally have no damn clue, what did I do
on the system.
This problem occurs on my machine on 4-STABLE rebuilt (few times)
and cvsupped around Tue Dec 23 22:58:01 CET 2003.
Anyway, here is promised testcase. I made it look as similar to code
found in the files I mentioned in "Full description". Of course this can
not be explictly compared to locale in libc (many factors are different).
Compiling this with AFTER_MY_PATCH set to 0 issues a warning, which is
not present in warnings generated by libc compile (even with -Wall).
This might be because of the way __part_load_locale is called.
Remember, that I have not tested if libc enters __part_load_locale
with the same arguments only one time or many times; I have just
found something, that looks like a serious bug to me -- and created
patches, that get rid of it. I have no more time for testing this
(I have work to do, who hasn't) -- but if you find this situation
interesting, feel free to e-mail me, I will help with tracking of this
bug to the extent of shell account on my machine.
testcase.c:
#include <stdio.h>
#include <stdlib.h>
/*
* AFTER_MY_PATCH:
*
* when defined to "0", segfaults on my setup, which is:
* FreeBSD mainframe.w.lub.pl 4.9-STABLE FreeBSD 4.9-STABLE #0: Tue Dec 23 20:45:53 CET 2003 root at mainframe.w.lub.pl:/usr/obj/usr/src/sys/MP-UX i386
* (a stripped GENERIC)
*
* doc at mainframe:~> gcc --version
* 2.95.4
* doc at mainframe:~> ld --version
* GNU ld version 2.12.1 [FreeBSD] 2002-07-20
*/
#define AFTER_MY_PATCH 1
static char *_whatever_locale_buf;
FAKE__part_load_locale(char **foo) {
static char *a = "worked";
#if AFTER_MY_PATCH == 1
puts("here");
if (_whatever_locale_buf != NULL) {
puts("and here!");
if (*_whatever_locale_buf != NULL) {
puts("still here!");
if (strcmp("worked", _whatever_locale_buf)==0) {
puts("I was already used. This is okay.");
exit(0);
}
}
}
puts("after here!");
#else
puts("before checking");
if (*_whatever_locale_buf != NULL && strcmp("worked", *_whatever_locale_buf)==0) {
puts("I was already used. This is okay.");
exit(0);
}
puts("after checking");
#endif
puts("about to set variable");
*foo = a;
puts("did it! returning!");
}
main(){
FAKE__part_load_locale(&_whatever_locale_buf);
puts(_whatever_locale_buf); /* should output "worked" */
FAKE__part_load_locale(&_whatever_locale_buf); /* should output "already used" and exit */
}
>Fix:
I have only partial knowledge about the calling proces in ldpart.c, so
those patches might be only a walk-around and not a fix. Please remember
about it.
diff -ur src/lib/libc/stdtime.orig/timelocal.c src/lib/libc/stdtime/timelocal.c
--- src/lib/libc/stdtime.orig/timelocal.c Wed Dec 24 00:42:32 2003
+++ src/lib/libc/stdtime/timelocal.c Wed Dec 24 00:42:53 2003
@@ -35,7 +35,7 @@
static struct lc_time_T _time_locale;
static int _time_using_locale;
-static char *time_locale_buf;
+static char *time_locale_buf = NULL;
#define LCTIME_SIZE (sizeof(struct lc_time_T) / sizeof(char *))
@@ -111,7 +111,7 @@
__time_load_locale(const char *name)
{
return (__part_load_locale(name, &_time_using_locale,
- time_locale_buf, "LC_TIME",
+ &time_locale_buf, "LC_TIME",
LCTIME_SIZE, LCTIME_SIZE,
(const char **)&_time_locale));
}
diff -ur src/lib/libc/locale.orig/ldpart.c src/lib/libc/locale/ldpart.c
--- src/lib/libc/locale.orig/ldpart.c Tue Dec 23 23:53:32 2003
+++ src/lib/libc/locale/ldpart.c Wed Dec 24 00:29:58 2003
@@ -66,9 +66,15 @@
/*
* If the locale name is the same as our cache, use the cache.
*/
- if (*locale_buf != NULL && strcmp(name, *locale_buf) == 0) {
- *using_locale = 1;
- return (_LDP_CACHE);
+ if (locale_buf != NULL) {
+ if (*locale_buf != NULL) {
+ if (strcmp(name, locale_buf) == 0) {
+ *using_locale = 1;
+ return (_LDP_CACHE);
+ }
+
+ }
+
}
/*
@@ -121,8 +127,12 @@
/*
* Record the successful parse in the cache.
*/
- if (*locale_buf != NULL)
- free(*locale_buf);
+ if (locale_buf != NULL) {
+ if (*locale_buf != NULL) {
+ free(*locale_buf);
+ *locale_buf = NULL;
+ }
+ }
*locale_buf = lbuf;
for (p = *locale_buf, i = 0; i < num_lines; i++)
dst_localebuf[i] = (p += strlen(p) + 1);
diff -ur src/lib/libc/locale.orig/lmessages.c src/lib/libc/locale/lmessages.c
--- src/lib/libc/locale.orig/lmessages.c Tue Dec 23 23:53:32 2003
+++ src/lib/libc/locale/lmessages.c Tue Dec 23 23:56:30 2003
@@ -28,7 +28,7 @@
__FBSDID("$FreeBSD: src/lib/libc/locale/lmessages.c,v 1.9.2.2 2002/08/12 11:17:37 ache Exp $");
#include <stddef.h>
-
+#include <stdlib.h>
#include "lmessages.h"
#include "ldpart.h"
@@ -47,7 +47,7 @@
static struct lc_messages_T _messages_locale;
static int _messages_using_locale;
-static char *_messages_locale_buf;
+static char *_messages_locale_buf = NULL;
int
__messages_load_locale(const char *name)
@@ -55,7 +55,7 @@
int ret;
ret = __part_load_locale(name, &_messages_using_locale,
- _messages_locale_buf, "LC_MESSAGES",
+ &_messages_locale_buf, "LC_MESSAGES",
LCMESSAGES_SIZE_FULL, LCMESSAGES_SIZE_MIN,
(const char **)&_messages_locale);
if (ret == _LDP_LOADED) {
diff -ur src/lib/libc/locale.orig/lmonetary.c src/lib/libc/locale/lmonetary.c
--- src/lib/libc/locale.orig/lmonetary.c Tue Dec 23 23:53:32 2003
+++ src/lib/libc/locale/lmonetary.c Wed Dec 24 00:10:09 2003
@@ -60,7 +60,7 @@
static struct lc_monetary_T _monetary_locale;
static int _monetary_using_locale;
-static char *_monetary_locale_buf;
+static char *_monetary_locale_buf = NULL;
static char
cnv(const char *str)
@@ -78,7 +78,7 @@
int ret;
ret = __part_load_locale(name, &_monetary_using_locale,
- _monetary_locale_buf, "LC_MONETARY",
+ &_monetary_locale_buf, "LC_MONETARY",
LCMONETARY_SIZE, LCMONETARY_SIZE,
(const char **)&_monetary_locale);
if (ret != _LDP_ERROR)
diff -ur src/lib/libc/locale.orig/lnumeric.c src/lib/libc/locale/lnumeric.c
--- src/lib/libc/locale.orig/lnumeric.c Tue Dec 23 23:53:32 2003
+++ src/lib/libc/locale/lnumeric.c Tue Dec 23 23:56:21 2003
@@ -28,6 +28,7 @@
__FBSDID("$FreeBSD: src/lib/libc/locale/lnumeric.c,v 1.10.2.2 2002/08/12 11:17:38 ache Exp $");
#include <limits.h>
+#include <stdlib.h>
#include "lnumeric.h"
#include "ldpart.h"
@@ -46,7 +47,7 @@
static struct lc_numeric_T _numeric_locale;
static int _numeric_using_locale;
-static char *_numeric_locale_buf;
+static char *_numeric_locale_buf = NULL;
int
__numeric_load_locale(const char *name)
@@ -54,7 +55,7 @@
int ret;
ret = __part_load_locale(name, &_numeric_using_locale,
- _numeric_locale_buf, "LC_NUMERIC",
+ &_numeric_locale_buf, "LC_NUMERIC",
LCNUMERIC_SIZE, LCNUMERIC_SIZE,
(const char **)&_numeric_locale);
if (ret != _LDP_ERROR)
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list