FreeBSD-SA-09:07: db(3) Information Leaks
This was published on 22 April 2009 by FreeBSD project and credits go to Jaakko Heinonen, Xin LI. This vulnerability affects FreeBSD 6.0 up to 7.1 release. The bug is on a standard C library’s function. This routine db(3) is used as an interface to the Berkeley DB 1.85 database files. These database files include (on FreeBSD):
- /etc/pwd.db which provides access to /etc/passwd
- /etc/spwd.db which provides access to /etc/master.passwd
The following code is part of the FreeBSD 7.1 release:
151 /*
152 * We need a buffer for this page. Either allocate one, or evict a resident
153 * one (if we have as many buffers as we're allowed) and put this one in.
154 *
155 * If newbuf finds an error (returning NULL), it also sets errno.
156 */
157 static BUFHEAD *
158 newbuf(hashp, addr, prev_bp)
159 HTAB *hashp;
160 u_int32_t addr;
161 BUFHEAD *prev_bp;
162 {
163 BUFHEAD *bp; /* The buffer we're going to use */
164 BUFHEAD *xbp; /* Temp pointer */
...
176 if (hashp->nbufs || (bp->flags & BUF_PIN)) {
177 /* Allocate a new one */
178 if ((bp = (BUFHEAD *)malloc(sizeof(BUFHEAD)) == NULL)
179 return (NULL);
180 #ifdef PURIFY
181 memset(bp, 0xff, sizeof(BUFHEAD));
182 #endif
183 if ((bp->page = (char *)malloc(hashp->BSIZE)) == NULL) {
184 free(bp);
185 return (NULL);
186 }
187 #ifdef PURIFY
188 memset(bp->page, 0xff, hashp->BSIZE);
189 #endif
...
284 return (bp);
285 }
This function can be found at lib/libc/db/hash/hash_buf.c and it’s used internally in db(3) code to allocate buffers for hash tables. At line 178 it uses malloc(3) to allocate memory of BUFHEAD bytes. This structure is defined at hash.h like this:
41 /* Buffer Management structures */
42 typedef struct _bufhead BUFHEAD;
43
44 struct _bufhead {
45 BUFHEAD *prev; /* LRU links */
46 BUFHEAD *next; /* LRU links */
47 BUFHEAD *ovfl; /* Overflow page buffer header */
48 u_int32_t addr; /* Address of this page */
49 char *page; /* Actual page data */
50 char flags;
51 #define BUF_MOD 0x0001
52 #define BUF_DISK 0x0002
53 #define BUF_BUCKET 0x0004
54 #define BUF_PIN 0x0008
55 };
So.. it is a usual doubly linked list structure. As we all know malloc(3) does not clear the memory it allocates for efficiency. Here is the exact description from its man page:
The malloc() function allocates size bytes of uninitialized memory. The allocated space is suitably aligned (after possible pointer coercion) for storage of any type of object.
However, if libc is compiled with PURIFY set then the allocated space will be initialized to 0xFF (lines 180-182). The same bug arises at line 183 and once again, PURIFY fixes it if defined (at lines 187-189). To fix this bug which can lead to information leak the following patch was applied:
/* Allocate a new one */
- if ((bp = (BUFHEAD *)malloc(sizeof(BUFHEAD))) == NULL)
+ if ((bp = (BUFHEAD *)calloc(1, sizeof(BUFHEAD))) == NULL)
return (NULL);
#ifdef PURIFY
memset(bp, 0xff, sizeof(BUFHEAD));
#endif
- if ((bp->page = (char *)malloc(hashp->BSIZE)) == NULL) {
+ if ((bp->page = (char *)calloc(1, hashp->BSIZE)) == NULL) {
free(bp);
Unlike malloc(3), calloc(3) initializes the allocated memory to zero before passing its pointer. In addition to this, the following was patched:
312 extern int 313 __buf_free(hashp, do_free, to_disk) 314 HTAB *hashp; 315 int do_free, to_disk; 316 { 317 BUFHEAD *bp; ... 330 /* Check if we are freeing stuff */ 331 if (do_free) 332 if (bp->page) 333 free(bp->page); 334 BUF_REMOVE(bp); 335 free(bp); 336 bp = LRU; ...
This function is used to free buffers internally and can be found at lib/libc/db/hash/hash_buf.c. Here, even though bp->page is freed (line 333), its data never get cleared from memory which might also lead to information leak. This was patched like this:
if (do_free) { - if (bp->page) + if (bp->page) { + (void)memset(bp->page, 0, hashp->BSIZE); free(bp->page); + } BUF_REMOVE(bp);
Another similar information leak was present at lib/libc/db/btree/bt_split.c on the following routine:
324 /*
325 * BT_PAGE -- Split a non-root page of a btree.
326 *
327 * Parameters:
328 * t: tree
329 * h: root page
330 * lp: pointer to left page pointer
331 * rp: pointer to right page pointer
332 * skip: pointer to index to leave open
333 * ilen: insert length
334 *
335 * Returns:
336 * Pointer to page in which to insert or NULL on error.
337 */
338 static PAGE *
339 bt_page(t, h, lp, rp, skip, ilen)
340 BTREE *t;
341 PAGE *h, **lp, **rp;
342 indx_t *skip;
343 size_t ilen;
344 {
...
383 /* Put the new left page for the split into place. */
384 if ((l = (PAGE *)malloc(t->bt_psize)) == NULL) {
385 mpool_put(t->bt_mp, r, 0);
386 return (NULL);
...
423 return (tp);
424 }
Where malloc(3) is used at line 384 and pointer l contains uninitialized memory. This was replaced with calloc(3) simply like that:
/* Put the new left page for the split into place. */
- if ((l = (PAGE *)malloc(t->bt_psize)) == NULL) {
+ if ((l = (PAGE *)calloc(1, t->bt_psize)) == NULL) {
mpool_put(t->bt_mp, r, 0);
The last vulnerable dynamic memory allocation was at lib/libc/db/mpool/mpool.c where this code resides:
302 /*
303 * mpool_bkt
304 * Get a page from the cache (or create one).
305 */
306 static BKT *
307 mpool_bkt(mp)
308 MPOOL *mp;
309 {
310 struct _hqh *head;
311 BKT *bp;
...
345
346 new: if ((bp = (BKT *)malloc(sizeof(BKT) + mp->pagesize)) == NULL)
347 return (NULL);
...
356 return (bp);
357 }
Where the malloc(3) call was replaced like this:
-new: if ((bp = (BKT *)malloc(sizeof(BKT) + mp->pagesize)) == NULL)
+new: if ((bp = (BKT *)calloc(1, sizeof(BKT) + mp->pagesize)) == NULL)
return (NULL);
#ifdef STATISTICS
Using the above information leaks a privileged application that uses db(3) might be able to either leak information to the user or inside the database files it manipulates. It won’t be hard to find whether this is possible or not after you have located applications using this library routine. For example, programs using getpwent(3) are also vulnerable since this routine is internally using dbopen() to open the password database file.

Leave a Reply