/* Check for backup and repeat */ // 假如当前文件流处于备份模式,那么从备份缓冲区切换回主缓冲区,不用理会 if (_IO_in_backup(fp)) { _IO_switch_to_main_get_area(fp); continue; }
/* If we now want less than a buffer, underflow and repeat the copy. Otherwise, _IO_SYSREAD directly to the user buffer. */ // 假如说文件流缓冲区已经建立,而且请求的字节数是小于_IO_buf_base和_IO_buf_end之间的缓冲区大小的,那么调用__underflow来读取数据 if (fp->_IO_buf_base && want < (size_t)(fp->_IO_buf_end - fp->_IO_buf_base)) { // 实际上是调用__underflow来读取数据 if (__underflow(fp) == EOF) break;
// 位于libio/fileops.c int _IO_new_file_underflow(_IO_FILE *fp) { _IO_ssize_t count; #if 0 /* SysV does not make this test; take it out for compatibility */ if (fp->_flags & _IO_EOF_SEEN) return (EOF); #endif
if (fp->_IO_buf_base == NULL) // 再次检查是否缓冲区未建立。 { /* Maybe we already have a push back pointer. */ if (fp->_IO_save_base != NULL) { free(fp->_IO_save_base); fp->_flags &= ~_IO_IN_BACKUP; } _IO_doallocbuf(fp); }
/* Flush all line buffered files before reading. */ /* FIXME This can/should be moved to genops ?? */ // 在读取之前对文件流进行一个刷新,需要flag含有0x208 if (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED)) { #if 0 _IO_flush_all_linebuffered (); #else /* We used to flush all line-buffered stream. This really isn't required by any standard. My recollection is that traditional Unix systems did this for stdout. stderr better not be line buffered. So we do just that here explicitly. --drepper */ _IO_acquire_lock(_IO_stdout);
/* This is very tricky. We have to adjust those pointers before we call _IO_SYSREAD () since we may longjump () out while waiting for input. Those pointers may be screwed up. H.J. */ // 由于不存在要读和要写的数据(前面已经检查),那么将这些指针都设置为缓冲区开始的位置 fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_buf_base; fp->_IO_read_end = fp->_IO_buf_base; fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end = fp->_IO_buf_base;
// 使用read系统调用来进行读取。内部是读取了fp的fileno,因此我们将fileno劫持为0时,其将会使用stdin标准输入流进行读取。 // 读到的位置是fp->_IO_buf_base,读取的大小是这个缓冲区的大小。 count = _IO_SYSREAD(fp, fp->_IO_buf_base, fp->_IO_buf_end - fp->_IO_buf_base); if (count <= 0) { if (count == 0) fp->_flags |= _IO_EOF_SEEN; else fp->_flags |= _IO_ERR_SEEN, count = 0; } fp->_IO_read_end += count; if (count == 0) { /* If a stream is read to EOF, the calling application may switch active handles. As a result, our offset cache would no longer be valid, so unset it. */ fp->_offset = _IO_pos_BAD; return EOF; } if (fp->_offset != _IO_pos_BAD) _IO_pos_adjust(fp->_offset, count); return *(unsignedchar *)fp->_IO_read_ptr; }
if(*secret == 0){ printf("Dave, my mind is going...\n"); }else{ printf("The secret is %s, how dare you?\n", secret); }
fclose(fp); return; }
intmain() { char *secret = (char *)malloc(0x20); memset(secret, 0, 0x20); printf("--- Let's see the normal process... --- \n"); normal_process(secret); printf("--- Now we try to make a arbitry write... ---\n"); arbitry_write(secret); return0; }
// 位于/libio/fwrite.c _IO_size_t _IO_fwrite(constvoid *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp) { _IO_size_t request = size * count; // 计算请求的字节数 _IO_size_t written = 0; CHECK_FILE(fp, 0); if (request == 0) // 请求字节为0,那么无事发生,直接返回 return0; _IO_acquire_lock(fp); // 文件操作,需要加锁 if (_IO_vtable_offset(fp) != 0 || _IO_fwide(fp, -1) == -1) // 存在vtable。说明已经初始化了 written = _IO_sputn(fp, (constchar *)buf, request); // 调用written进行输出 _IO_release_lock(fp); /* We have written all of the input in case the return value indicates this or EOF is returned. The latter is a special case where we simply did not manage to flush the buffer. But the data is in the buffer and therefore written as far as fwrite is concerned. */ if (written == request || written == EOF) // 输出的字节数等于请求的字节数,返回请求的变量单位的数量 return count; else return written / size; // 否则返回实际输出的变量单位的数量 }
if (n <= 0) // 若请求字节数小于0,直接返回 return0; /* This is an optimized implementation. If the amount to be written straddles a block boundary (or the filebuf is unbuffered), use sys_write directly. */
/* First figure out how much space is available in the buffer. */ // 假如是行缓冲,且正在写入数据。第二个标志位很难满足,不会进入这个分支 // 而且文件流、标准输入流默认都是全缓冲 if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING)) { count = f->_IO_buf_end - f->_IO_write_ptr; if (count >= n) { constchar *p; for (p = s + n; p > s;) { if (*--p == '\n') { count = p - s + 1; must_flush = 1; break; } } } } elseif (f->_IO_write_end > f->_IO_write_ptr) // 当_IO_write_end大于_IO_write_ptr时说明当前还有数据没写到缓冲区,要避免 count = f->_IO_write_end - f->_IO_write_ptr; // 没写到缓冲区的数据
/* Then fill the buffer. */ if (count > 0) // 会写到缓冲区 { if (count > to_do) count = to_do; #ifdef _LIBC f->_IO_write_ptr = __mempcpy(f->_IO_write_ptr, s, count); #else memcpy(f->_IO_write_ptr, s, count); f->_IO_write_ptr += count; #endif s += count; to_do -= count; } if (to_do + must_flush > 0) // to_do是还需要写的字节数,肯定大于0 { _IO_size_t block_size, do_write; /* Next flush the (full) buffer. */ if (_IO_OVERFLOW(f, EOF) == EOF) // 调用_IO_OVEFLOW来设置和刷新缓冲区 /* If nothing else has to be written we must not signal the caller that everything has been written. */ return to_do == 0 ? EOF : n - to_do;
// 位于/libio/fileops.c int _IO_new_file_overflow(_IO_FILE *f, int ch) { // 假如包含_IO_NO_WRITES,值为8,那么报错并返回 if (f->_flags & _IO_NO_WRITES) /* SET ERROR */ { f->_flags |= _IO_ERR_SEEN; __set_errno(EBADF); return EOF; } // 假如正在输出数据,或者缓冲区未建立。正常情况下是未建立缓冲区的,因此会进入分支,分支会采取大量措施建立缓冲区不可控 // 因此最好提前设置_IO_write_base的值,并设置f->_flags包含_IO_CURRENTLY_PUTTING,其值为8 if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL) { /* Allocate a buffer if needed. */ if (f->_IO_write_base == NULL) { _IO_doallocbuf(f); _IO_setg(f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base); } /* Otherwise must be currently reading. If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end, logically slide the buffer forwards one block (by setting the read pointers to all point at the beginning of the block). This makes room for subsequent output. Otherwise, set the read pointers to _IO_read_end (leaving that alone, so it can continue to correspond to the external position). */ if (__glibc_unlikely(_IO_in_backup(f))) { size_t nbackup = f->_IO_read_end - f->_IO_read_ptr; _IO_free_backup_area(f); f->_IO_read_base -= MIN(nbackup, f->_IO_read_base - f->_IO_buf_base); f->_IO_read_ptr = f->_IO_read_base; }