From owner-freebsd-bugs@FreeBSD.ORG Tue Dec 23 17:30:15 2003 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 8FB4816A4CE for ; Tue, 23 Dec 2003 17:30:15 -0800 (PST) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id E9EEB43D53 for ; Tue, 23 Dec 2003 17:30:10 -0800 (PST) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) hBO1UAFR024562 for ; Tue, 23 Dec 2003 17:30:10 -0800 (PST) (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.12.10/8.12.10/Submit) id hBO1UAdP024561; Tue, 23 Dec 2003 17:30:10 -0800 (PST) (envelope-from gnats) Resent-Date: Tue, 23 Dec 2003 17:30:10 -0800 (PST) Resent-Message-Id: <200312240130.hBO1UAdP024561@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Michal Pasternak Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 457D616A4CE for ; Tue, 23 Dec 2003 17:21:17 -0800 (PST) Received: from www.freebsd.org (www.freebsd.org [216.136.204.117]) by mx1.FreeBSD.org (Postfix) with ESMTP id 02EB343D5D for ; Tue, 23 Dec 2003 17:21:14 -0800 (PST) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (localhost [127.0.0.1]) by www.freebsd.org (8.12.10/8.12.10) with ESMTP id hBO1LDdL088337 for ; Tue, 23 Dec 2003 17:21:13 -0800 (PST) (envelope-from nobody@www.freebsd.org) Received: (from nobody@localhost) by www.freebsd.org (8.12.10/8.12.10/Submit) id hBO1LDFA088336; Tue, 23 Dec 2003 17:21:13 -0800 (PST) (envelope-from nobody) Message-Id: <200312240121.hBO1LDFA088336@www.freebsd.org> Date: Tue, 23 Dec 2003 17:21:13 -0800 (PST) From: Michal Pasternak To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-2.0 Subject: 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 X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 24 Dec 2003 01:30:15 -0000 >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@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 #include /* * 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@mainframe.w.lub.pl:/usr/obj/usr/src/sys/MP-UX i386 * (a stripped GENERIC) * * doc@mainframe:~> gcc --version * 2.95.4 * doc@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 - +#include #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 +#include #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: