Bug 60533

Summary: 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
Product: Base System Reporter: dotz <dotz>
Component: binAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 4.9-STABLE   
Hardware: Any   
OS: Any   
Attachments:
Description Flags
file.diff none

Description dotz 2003-12-24 01:30:10 UTC
*****************************************************************

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.

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.

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@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 */
}
Comment 1 Tim Robbins freebsd_committer freebsd_triage 2004-08-24 06:04:43 UTC
State Changed
From-To: open->closed

This problem is believed to have been fixed in 4.10-R and -STABLE.