/ Check-in [c54de1f5]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Change the pcache1.c implementation so that the "header" occurs at the end of page buffer, not at the beginning. This insures that the 20 bytes immediately following the page buffer are mapped if a read of the page buffer overruns due to a malformed cell. (CVS 6711)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:c54de1f54080de7e134d7b562498abb5337a0a46
User & Date: drh 2009-06-03 21:04:36
Context
2009-06-04
00:11
Fix two instances where a malformed header in a database file (in other words a corrupt database file) can lead to an assertion fault. (CVS 6712) check-in: 4e92c7f2 user: drh tags: trunk
2009-06-03
21:04
Change the pcache1.c implementation so that the "header" occurs at the end of page buffer, not at the beginning. This insures that the 20 bytes immediately following the page buffer are mapped if a read of the page buffer overruns due to a malformed cell. (CVS 6711) check-in: c54de1f5 user: drh tags: trunk
17:26
Add corruptD.test, a container for testing the "cell overflow" problem. Also shuffle a small amount of code in BtreeInitPage() to check that the page header pointer to the start of the cell offset array is set to a sane value. (CVS 6710) check-in: 7fa5d3cb user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
1138
1139
1140
1141
1142
1143
1144
1145































1146
1147
1148
1149
1150
1151
1152
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.611 2009/06/03 17:26:18 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
#include "btreeInt.h"

................................................................................
    pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf;
    top = get2byte(&data[hdr+5]);
    pPage->nCell = get2byte(&data[hdr+3]);
    if( pPage->nCell>MX_CELL(pBt) ){
      /* To many cells for a single page.  The page must be corrupt */
      return SQLITE_CORRUPT_BKPT;
    }
  































    /* Compute the total free space on the page */
    pc = get2byte(&data[hdr+1]);
    nFree = data[hdr+7] + top;
    while( pc>0 ){
      u16 next, size;
      if( pc>usableSize-4 ){
        /* Free block is off the page */







|







 







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.612 2009/06/03 21:04:36 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
#include "btreeInt.h"

................................................................................
    pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf;
    top = get2byte(&data[hdr+5]);
    pPage->nCell = get2byte(&data[hdr+3]);
    if( pPage->nCell>MX_CELL(pBt) ){
      /* To many cells for a single page.  The page must be corrupt */
      return SQLITE_CORRUPT_BKPT;
    }

    /* A malformed database page might cause use to read past the end
    ** of page when parsing a cell.  
    **
    ** The following block of code checks early to see if a cell extends
    ** past the end of a page boundary and causes SQLITE_CORRUPT to be 
    ** returned if it does.
    */
#if defined(SQLITE_OVERREAD_CHECK) || 1
    {
      int iCellFirst;   /* First allowable cell index */
      int iCellLast;    /* Last possible cell index */
      int i;            /* Index into the cell pointer array */
      int sz;           /* Size of a cell */

      iCellFirst = cellOffset + 2*pPage->nCell;
      iCellLast = usableSize - 4;
      if( !pPage->leaf ) iCellLast--;
      for(i=0; i<pPage->nCell; i++){
        pc = get2byte(&data[cellOffset+i*2]);
        if( pc<iCellFirst || pc>iCellLast ){
          return SQLITE_CORRUPT_BKPT;
        }
        sz = cellSizePtr(pPage, &data[pc]);
        if( pc+sz>usableSize ){
          return SQLITE_CORRUPT_BKPT;
        }
      }
    }  
#endif


    /* Compute the total free space on the page */
    pc = get2byte(&data[hdr+1]);
    nFree = data[hdr+7] + top;
    while( pc>0 ){
      u16 next, size;
      if( pc>usableSize-4 ){
        /* Free block is off the page */

Changes to src/pcache1.c.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
..
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
..
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
...
199
200
201
202
203
204
205
206

207

208
209
210


211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
...
556
557
558
559
560
561
562
563
564
565
566
567
568
569

570
571
572
573
574
575
576
...
583
584
585
586
587
588
589
590
591

592
593
594
595
596
597
598
...
626
627
628
629
630
631
632
633
634
635
636

637
638
639
640
641
642
643
...
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
**
** This file implements the default page cache implementation (the
** sqlite3_pcache interface). It also contains part of the implementation
** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features.
** If the default page cache implementation is overriden, then neither of
** these two features are available.
**
** @(#) $Id: pcache1.c,v 1.15 2009/05/22 11:12:23 drh Exp $
*/

#include "sqliteInt.h"

typedef struct PCache1 PCache1;
typedef struct PgHdr1 PgHdr1;
typedef struct PgFreeslot PgFreeslot;
................................................................................

  unsigned int iMaxKey;               /* Largest key seen since xTruncate() */
};

/*
** Each cache entry is represented by an instance of the following 
** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated 
** directly after the structure in memory (see the PGHDR1_TO_PAGE() 
** macro below).
*/
struct PgHdr1 {
  unsigned int iKey;             /* Key value (page number) */
  PgHdr1 *pNext;                 /* Next in hash table chain */
  PCache1 *pCache;               /* Cache that currently owns this page */
  PgHdr1 *pLruNext;              /* Next in LRU list of unpinned pages */
................................................................................
** alias "pcache1". This ensures that the WSD emulation is used when
** compiling for systems that do not support real WSD.
*/
#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g))

/*
** When a PgHdr1 structure is allocated, the associated PCache1.szPage
** bytes of data are located directly after it in memory (i.e. the total
** size of the allocation is sizeof(PgHdr1)+PCache1.szPage byte). The
** PGHDR1_TO_PAGE() macro takes a pointer to a PgHdr1 structure as
** an argument and returns a pointer to the associated block of szPage
** bytes. The PAGE_TO_PGHDR1() macro does the opposite: its argument is
** a pointer to a block of szPage bytes of data and the return value is
** a pointer to the associated PgHdr1 structure.
**
**   assert( PGHDR1_TO_PAGE(PAGE_TO_PGHDR1(X))==X );
*/
#define PGHDR1_TO_PAGE(p) (void *)(&((unsigned char *)p)[sizeof(PgHdr1)])
#define PAGE_TO_PGHDR1(p) (PgHdr1 *)(&((unsigned char *)p)[-1*(int)sizeof(PgHdr1)])

/*
** Macros to enter and leave the global LRU mutex.
*/
#define pcache1EnterMutex() sqlite3_mutex_enter(pcache1.mutex)
#define pcache1LeaveMutex() sqlite3_mutex_leave(pcache1.mutex)

................................................................................
}

/*
** Allocate a new page object initially associated with cache pCache.
*/
static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
  int nByte = sizeof(PgHdr1) + pCache->szPage;
  PgHdr1 *p = (PgHdr1 *)pcache1Alloc(nByte);

  if( p ){

    if( pCache->bPurgeable ){
      pcache1.nCurrentPage++;
    }


  }
  return p;
}

/*
** Free a page object allocated by pcache1AllocPage().
*/
static void pcache1FreePage(PgHdr1 *p){
  if( p ){
    if( p->pCache->bPurgeable ){
      pcache1.nCurrentPage--;
    }
    pcache1Free(p);
  }
}

/*
** Malloc function used by SQLite to obtain space from the buffer configured
** using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer
** exists, this function falls back to sqlite3Malloc().
................................................................................
  */
  if( !pPage ){
    pPage = pcache1AllocPage(pCache);
  }

  if( pPage ){
    unsigned int h = iKey % pCache->nHash;
    *(void **)(PGHDR1_TO_PAGE(pPage)) = 0;
    pCache->nPage++;
    pPage->iKey = iKey;
    pPage->pNext = pCache->apHash[h];
    pPage->pCache = pCache;
    pPage->pLruPrev = 0;
    pPage->pLruNext = 0;

    pCache->apHash[h] = pPage;
  }

fetch_out:
  if( pPage && iKey>pCache->iMaxKey ){
    pCache->iMaxKey = iKey;
  }
................................................................................
/*
** Implementation of the sqlite3_pcache.xUnpin method.
**
** Mark a page as unpinned (eligible for asynchronous recycling).
*/
static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
  PCache1 *pCache = (PCache1 *)p;
  PgHdr1 *pPage = PAGE_TO_PGHDR1(pPg);


  pcache1EnterMutex();

  /* It is an error to call this function if the page is already 
  ** part of the global LRU list.
  */
  assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
  assert( pcache1.pLruHead!=pPage && pcache1.pLruTail!=pPage );
................................................................................
static void pcache1Rekey(
  sqlite3_pcache *p,
  void *pPg,
  unsigned int iOld,
  unsigned int iNew
){
  PCache1 *pCache = (PCache1 *)p;
  PgHdr1 *pPage = PAGE_TO_PGHDR1(pPg);
  PgHdr1 **pp;
  unsigned int h; 
  assert( pPage->iKey==iOld );


  pcache1EnterMutex();

  h = iOld%pCache->nHash;
  pp = &pCache->apHash[h];
  while( (*pp)!=pPage ){
    pp = &(*pp)->pNext;
................................................................................
*/
int sqlite3PcacheReleaseMemory(int nReq){
  int nFree = 0;
  if( pcache1.pStart==0 ){
    PgHdr1 *p;
    pcache1EnterMutex();
    while( (nReq<0 || nFree<nReq) && (p=pcache1.pLruTail) ){
      nFree += sqlite3MallocSize(p);
      pcache1PinPage(p);
      pcache1RemoveFromHash(p);
      pcache1FreePage(p);
    }
    pcache1LeaveMutex();
  }
  return nFree;







|







 







|







 







|







|

|
|







 







|
>
|
>



>
>












|







 







<






>







 







|
|
>







 







|



>







 







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
..
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
..
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
...
560
561
562
563
564
565
566

567
568
569
570
571
572
573
574
575
576
577
578
579
580
...
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
...
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
...
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
**
** This file implements the default page cache implementation (the
** sqlite3_pcache interface). It also contains part of the implementation
** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features.
** If the default page cache implementation is overriden, then neither of
** these two features are available.
**
** @(#) $Id: pcache1.c,v 1.16 2009/06/03 21:04:36 drh Exp $
*/

#include "sqliteInt.h"

typedef struct PCache1 PCache1;
typedef struct PgHdr1 PgHdr1;
typedef struct PgFreeslot PgFreeslot;
................................................................................

  unsigned int iMaxKey;               /* Largest key seen since xTruncate() */
};

/*
** Each cache entry is represented by an instance of the following 
** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated 
** directly before this structure in memory (see the PGHDR1_TO_PAGE() 
** macro below).
*/
struct PgHdr1 {
  unsigned int iKey;             /* Key value (page number) */
  PgHdr1 *pNext;                 /* Next in hash table chain */
  PCache1 *pCache;               /* Cache that currently owns this page */
  PgHdr1 *pLruNext;              /* Next in LRU list of unpinned pages */
................................................................................
** alias "pcache1". This ensures that the WSD emulation is used when
** compiling for systems that do not support real WSD.
*/
#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g))

/*
** When a PgHdr1 structure is allocated, the associated PCache1.szPage
** bytes of data are located directly before it in memory (i.e. the total
** size of the allocation is sizeof(PgHdr1)+PCache1.szPage byte). The
** PGHDR1_TO_PAGE() macro takes a pointer to a PgHdr1 structure as
** an argument and returns a pointer to the associated block of szPage
** bytes. The PAGE_TO_PGHDR1() macro does the opposite: its argument is
** a pointer to a block of szPage bytes of data and the return value is
** a pointer to the associated PgHdr1 structure.
**
**   assert( PGHDR1_TO_PAGE(PAGE_TO_PGHDR1(pCache, X))==X );
*/
#define PGHDR1_TO_PAGE(p)    (void*)(((char*)p) - p->pCache->szPage)
#define PAGE_TO_PGHDR1(c, p) (PgHdr1*)(((char*)p) + c->szPage)

/*
** Macros to enter and leave the global LRU mutex.
*/
#define pcache1EnterMutex() sqlite3_mutex_enter(pcache1.mutex)
#define pcache1LeaveMutex() sqlite3_mutex_leave(pcache1.mutex)

................................................................................
}

/*
** Allocate a new page object initially associated with cache pCache.
*/
static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
  int nByte = sizeof(PgHdr1) + pCache->szPage;
  void *pPg = pcache1Alloc(nByte);
  PgHdr1 *p;
  if( pPg ){
    p = PAGE_TO_PGHDR1(pCache, pPg);
    if( pCache->bPurgeable ){
      pcache1.nCurrentPage++;
    }
  }else{
    p = 0;
  }
  return p;
}

/*
** Free a page object allocated by pcache1AllocPage().
*/
static void pcache1FreePage(PgHdr1 *p){
  if( p ){
    if( p->pCache->bPurgeable ){
      pcache1.nCurrentPage--;
    }
    pcache1Free(PGHDR1_TO_PAGE(p));
  }
}

/*
** Malloc function used by SQLite to obtain space from the buffer configured
** using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer
** exists, this function falls back to sqlite3Malloc().
................................................................................
  */
  if( !pPage ){
    pPage = pcache1AllocPage(pCache);
  }

  if( pPage ){
    unsigned int h = iKey % pCache->nHash;

    pCache->nPage++;
    pPage->iKey = iKey;
    pPage->pNext = pCache->apHash[h];
    pPage->pCache = pCache;
    pPage->pLruPrev = 0;
    pPage->pLruNext = 0;
    *(void **)(PGHDR1_TO_PAGE(pPage)) = 0;
    pCache->apHash[h] = pPage;
  }

fetch_out:
  if( pPage && iKey>pCache->iMaxKey ){
    pCache->iMaxKey = iKey;
  }
................................................................................
/*
** Implementation of the sqlite3_pcache.xUnpin method.
**
** Mark a page as unpinned (eligible for asynchronous recycling).
*/
static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
  PCache1 *pCache = (PCache1 *)p;
  PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg);
 
  assert( pPage->pCache==pCache );
  pcache1EnterMutex();

  /* It is an error to call this function if the page is already 
  ** part of the global LRU list.
  */
  assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
  assert( pcache1.pLruHead!=pPage && pcache1.pLruTail!=pPage );
................................................................................
static void pcache1Rekey(
  sqlite3_pcache *p,
  void *pPg,
  unsigned int iOld,
  unsigned int iNew
){
  PCache1 *pCache = (PCache1 *)p;
  PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg);
  PgHdr1 **pp;
  unsigned int h; 
  assert( pPage->iKey==iOld );
  assert( pPage->pCache==pCache );

  pcache1EnterMutex();

  h = iOld%pCache->nHash;
  pp = &pCache->apHash[h];
  while( (*pp)!=pPage ){
    pp = &(*pp)->pNext;
................................................................................
*/
int sqlite3PcacheReleaseMemory(int nReq){
  int nFree = 0;
  if( pcache1.pStart==0 ){
    PgHdr1 *p;
    pcache1EnterMutex();
    while( (nReq<0 || nFree<nReq) && (p=pcache1.pLruTail) ){
      nFree += sqlite3MallocSize(PGHDR1_TO_PAGE(p));
      pcache1PinPage(p);
      pcache1RemoveFromHash(p);
      pcache1FreePage(p);
    }
    pcache1LeaveMutex();
  }
  return nFree;