ssongk
ssongk
ssongk
전체 방문자
오늘
어제

공지사항

  • resources
  • 분류 전체보기 (626)
    • CTF (24)
    • background (79)
      • fuzzing (5)
      • linux (29)
      • linux kernel (15)
      • windows (2)
      • web assembly (1)
      • embedded (0)
      • web (13)
      • crypto (9)
      • mobile (1)
      • AI (1)
      • etc.. (3)
    • write-up(pwn) (171)
      • dreamhack (102)
      • pwn.college (4)
      • pwnable.xyz (51)
      • pwnable.tw (3)
      • pwnable.kr (5)
      • G04T (6)
    • write-up(rev) (32)
      • dreamhack (24)
      • reversing.kr (8)
    • write-up(web) (195)
      • dreamhack (63)
      • LOS (40)
      • webhacking.kr (69)
      • websec.fr (3)
      • wargame.kr (6)
      • webgoat (1)
      • G04T (7)
      • suninatas (6)
    • write-up(crypto) (19)
      • dreamhack (16)
      • G04T (1)
      • suninatas (2)
    • write-up(forensic) (53)
      • dreamhack (5)
      • ctf-d (47)
      • suninatas (1)
    • write-up(misc) (13)
      • dreamhack (12)
      • suninatas (1)
    • development (31)
      • Linux (14)
      • Java (13)
      • Python (1)
      • C (2)
      • TroubleShooting (1)
    • 자격증 (8)
    • 이산수학 (1)
    • 정보보안 (0)
hELLO · Designed By 정상우.
ssongk

ssongk

background/linux

[glibc-2.27] _IO_FILE & fopen

2023. 1. 7. 23:19

드림핵 강의를 보고 glibc-2.27을 분석한 글 입니다.

(틀린 내용이 있을 수 있습니다)


먼저, 드림핵에서 소개하는 예제 코드는 다음과 같다.

// Name: iofile.c
// Compile: gcc -o iofile iofile.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>

void file_info(FILE *buf) {
  printf("_flags: %x\n", buf->_flags);
  printf("_fileno: %d", buf->_fileno);
}

int main() {
  FILE *fp;
  char buf[256];
  strcpy(buf, "THIS IS TESTFILE!");
  fp = fopen("testfile", "w");
  fwrite(buf, 1, strlen(buf), fp);
  file_info(fp);
  fclose(fp);
  return 0;
}

 


_IO_FILE

_IO_FILE은 리눅스 시스템의 표준 라이브러리에서 파일 스트림을 나타내기 위한 구조체이다.

파일을 열기 위한 fopen 함수를 사용할 때 힙 영역에 할당된다.

 

FILE.h를 보면 _IO_FILE 구조체를 데이터 타입으로 사용한다.
c에서 파일을 다룰 때 사용했던 FILE 타입은 ‘_IO_FILE 구조체의 구현’이라는 의미였던 걸로 이해했다.

#ifndef __FILE_defined
#define __FILE_defined 1

struct _IO_FILE;

/* The opaque type of streams.  This is the definition used elsewhere.  */
typedef struct _IO_FILE FILE;

#endif

 

_IO_FILE 구조체는 libio.h에서 정의되고 있으며 다음과 같은 구조를 가지고 있다.

struct _IO_FILE_plus
{
  FILE file;
  const struct _IO_jump_t *vtable;
};

struct _IO_FILE
{
  int _flags;		/* High-order word is _IO_MAGIC; rest is flags. */
  /* The following pointers correspond to the C++ streambuf protocol. */
  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;
  int _flags2;
  __off_t _old_offset; /* This used to be _offset but it's too small.  */
  /* 1+column number of pbase(); 0 is unknown. */
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];
  _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

 

각 멤버들의 의미는 다음과 같다.

_flags 파일에 대한 읽기/쓰기/추가 권한을 의미
0xfbad0000 값을 매직 값으로, 하위 2바이트는 비트 플래그로 사용됨 
_IO_read_ptr 파일 읽기 버퍼에 대한 포인터
_IO_read_end 파일 읽기 버퍼 주소의 끝을 가리키는 포인터
_IO_read_base 파일 읽기 버퍼 주소의 시작을 가리키는 포인터
_IO_write_base 파일 쓰기 버퍼 주소의 시작을 가리키는 포인터
_IO_write_ptr 쓰기 버퍼에 대한 포인터
_IO_write_end 파일 쓰기 버퍼 주소의 끝을 가리키는 포인터
_chain 프로세스의 _IO_FILE 구조체는 _chain 필드를 통해 링크드 리스트를 만듦
링크드 리스트의 헤더는 라이브러리의 전역 변수인 _IO_list_all에 저장됨
_fileno 파일 디스크립터의 값
_IO_jump_ t *vtable 파일 관련 작업을 수행하는 가상 함수 테이블

 


_IO_FILE:  _flags

_flags 멤버 변수는 파일의 성질을 나타내는 필드이다.

해당 필드는 fopen 함수로 파일을 열 때 전달한 모드에 따라 값이 설정된다.

_flags 변수를 구성하는 비트들은 libio.h에 정의되어 있다.

다음은 _flags의 일부이다.

#define _IO_MAGIC         0xFBAD0000 /* Magic number */
#define _IO_MAGIC_MASK    0xFFFF0000
#define _IO_USER_BUF          0x0001 /* Don't deallocate buffer on close. */
#define _IO_UNBUFFERED        0x0002
#define _IO_NO_READS          0x0004 /* Reading not allowed.  */
#define _IO_NO_WRITES         0x0008 /* Writing not allowed.  */
#define _IO_EOF_SEEN          0x0010
#define _IO_ERR_SEEN          0x0020
#define _IO_DELETE_DONT_CLOSE 0x0040 /* Don't call close(_fileno) on close.  */
#define _IO_LINKED            0x0080 /* In the list of all open files.  */
#define _IO_IN_BACKUP         0x0100
#define _IO_LINE_BUF          0x0200
#define _IO_TIED_PUT_GET      0x0400 /* Put and get pointer move in unison.  */
#define _IO_CURRENTLY_PUTTING 0x0800
#define _IO_IS_APPENDING      0x1000
#define _IO_IS_FILEBUF        0x2000
                           /* 0x4000  No longer used, reserved for compat.  */
#define _IO_USER_LOCK         0x8000

이 중 _IO_CURRENTLY_PUTTING(0x0800), _IO_IS_APPENDING(0x1000)를 주로 사용하나보다.

 

예제 코드를 컴파일하고 실행해보면 다음과 같은 결과가 출력되는데

$ ./iofile
_flags: fbad2c84
_fileno: 3

0xfbad2c84는 매직 넘버(_IO_MAGIC)를 포함한 각 권한을 의미한다.

(_IO_MAGIC, _IO_NO_READS, _IO_LINKED, _IO_TIED_PUT_GET, _IO_CURRENTLY_PUTTING, _IO_IS_FILEBUF)

 


_IO_FILE:  fopen

처음에 _IO_FILE은 ‘fopen 함수를 사용할 때 힙 영역에 할당된다’ 라고 했다.

fopen 함수는 iofopen.c에 정의되어 있으며 iofopen.c는 다음과 같다.

_IO_FILE * __fopen_maybe_mmap (_IO_FILE *fp)
{
#ifdef _G_HAVE_MMAP
  if ((fp->_flags2 & _IO_FLAGS2_MMAP) && (fp->_flags & _IO_NO_WRITES))
    {
      /* Since this is read-only, we might be able to mmap the contents
	 directly.  We delay the decision until the first read attempt by
	 giving it a jump table containing functions that choose mmap or
	 vanilla file operations and reset the jump table accordingly.  */

      if (fp->_mode <= 0)
	_IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps_maybe_mmap;
      else
	_IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps_maybe_mmap;
      fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_maybe_mmap;
    }
#endif
  return fp;
}

_IO_FILE * __fopen_internal (const char *filename, const char *mode, int is32)
{
  struct locked_FILE
  {
    struct _IO_FILE_plus fp;
#ifdef _IO_MTSAFE_IO
    _IO_lock_t lock;
#endif
    struct _IO_wide_data wd;
  } *new_f = (struct locked_FILE *) malloc (sizeof (struct locked_FILE));

  if (new_f == NULL)
    return NULL;
#ifdef _IO_MTSAFE_IO
  new_f->fp.file._lock = &new_f->lock;
#endif
  _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd, &_IO_wfile_jumps);
  _IO_JUMPS (&new_f->fp) = &_IO_file_jumps;
  _IO_new_file_init_internal (&new_f->fp);
#if  !_IO_UNIFIED_JUMPTABLES
  new_f->fp.vtable = NULL;
#endif
  if (_IO_file_fopen ((_IO_FILE *) new_f, filename, mode, is32) != NULL)
    return __fopen_maybe_mmap (&new_f->fp.file);

  _IO_un_link (&new_f->fp);
  free (new_f);
  return NULL;
}

_IO_FILE * _IO_new_fopen (const char *filename, const char *mode)
{
  return __fopen_internal (filename, mode, 1);
}

strong_alias (_IO_new_fopen, __new_fopen)
versioned_symbol (libc, _IO_new_fopen, _IO_fopen, GLIBC_2_1);
versioned_symbol (libc, __new_fopen, fopen, GLIBC_2_1);

크게 보면 __fopen_maybe_mmap, __fopen_internal, _IO_new_fopen 총 3개의 함수로 구성되어 있다.

 

_IO_new_fopen

strong_alias, versioned_symbol, weak_alias 함수를 통해 _IO_new_fopen 함수가 fopen으로 사용되는 것 같다.

_IO_FILE * _IO_new_fopen (const char *filename, const char *mode)
{
  return __fopen_internal (filename, mode, 1);
}

(strong_alias, versioned_symbol, weak_alias은 그냥 함수의 별칭을 지어주는 메크로인듯?)

https://codebrowser.dev/glibc/glibc/include/libc-symbols.h.html

 

libc-symbols.h source code [glibc/include/libc-symbols.h] - Codebrowser

998#define libc_ifunc(name, expr) __ifunc (name, name, expr, void, INIT_ARCH)

codebrowser.dev

 

__fopen_internal

fopen(_IO_new_fopen)이 호출되면 리턴에 있는 __fopen_internal이 실행된다.

그럼 이제 __fopen_internal 함수에 대해 알아보자.

_IO_FILE * __fopen_internal (const char *filename, const char *mode, int is32)
{
  struct locked_FILE
  {
    struct _IO_FILE_plus fp;
#ifdef _IO_MTSAFE_IO
    _IO_lock_t lock;
#endif
    struct _IO_wide_data wd;
  } *new_f = (struct locked_FILE *) malloc (sizeof (struct locked_FILE));

  if (new_f == NULL)
    return NULL;

#ifdef _IO_MTSAFE_IO
  new_f->fp.file._lock = &new_f->lock;
#endif

  _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd, &_IO_wfile_jumps);
  _IO_JUMPS (&new_f->fp) = &_IO_file_jumps;
  _IO_new_file_init_internal (&new_f->fp);

#if  !_IO_UNIFIED_JUMPTABLES
  new_f->fp.vtable = NULL;
#endif

  if (_IO_file_fopen ((_IO_FILE *) new_f, filename, mode, is32) != NULL)
    return __fopen_maybe_mmap (&new_f->fp.file);

  _IO_un_link (&new_f->fp);
  free (new_f);
  return NULL;
}

filename, mode, is32를 인수로 받고 시작한다.

함수가 시작되면 locked_FILE이 정의된다. _IO_FILE_plus 타입 변수 fp와 _IO_wide_data 타입 변수 wd를 멤버로 가진다. _IO_MTSAFE_IO가 정의되어 있으면 _IO_lock_t 타입 변수 lock도 멤버로 가진다. 

locked_FILE 구조체는 malloc으로 힙 영역에 할당된 뒤 new_f 변수에 주소를 저장한다.

만약 malloc이 제대로 되지 않아 new_f가 null이 되면 그냥 리턴으로 종료한다.

 

참고로 _IO_wide_data 구조체는 libio.h에 정의되어 있다.

#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T

...

/* 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. */

  __mbstate_t _IO_state;
  __mbstate_t _IO_last_state;
  struct _IO_codecvt _codecvt;

  wchar_t _shortbuf[1];

  const struct _IO_jump_t *_wide_vtable;
};
#endif

 

이제 _IO_no_init, _IO_JUMPS, _IO_new_file_init_internal 함수를 차례로 호출한다.

_IO_MTSAFE_IO가 정의되어 있으면 new_f의 lock을 멤버인 fp.file(_IO_FILE)에 새로운 멤버 _lock을 추가해 저장한다. (new_f → fp.file._lock = &new_f->lock;)

 

1. _IO_no_init

  _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd, &_IO_wfile_jumps);

_IO_no_init의 정의는 genops.c에 있으며 다음과 같다. 

void _IO_no_init (_IO_FILE *fp, int flags, int orientation,
	     struct _IO_wide_data *wd, const struct _IO_jump_t *jmp) {
  _IO_old_init (fp, flags);
  fp->_mode = orientation;

  if (orientation >= 0) {
      fp->_wide_data = wd;
      fp->_wide_data->_IO_buf_base = NULL;
      fp->_wide_data->_IO_buf_end = NULL;
      fp->_wide_data->_IO_read_base = NULL;
      fp->_wide_data->_IO_read_ptr = NULL;
      fp->_wide_data->_IO_read_end = NULL;
      fp->_wide_data->_IO_write_base = NULL;
      fp->_wide_data->_IO_write_ptr = NULL;
      fp->_wide_data->_IO_write_end = NULL;
      fp->_wide_data->_IO_save_base = NULL;
      fp->_wide_data->_IO_backup_base = NULL;
      fp->_wide_data->_IO_save_end = NULL;

      fp->_wide_data->_wide_vtable = jmp;
    }
  else
    /* Cause predictable crash when a wide function is called on a byte
       stream.  */
    fp->_wide_data = (struct _IO_wide_data *) -1L;
  fp->_freeres_list = NULL;
}

_IO_old_init의 정의는 genops.c에 _IO_no_init의 정의 바로 위에 있다.

void _IO_old_init (_IO_FILE *fp, int flags) {
  fp->_flags = _IO_MAGIC|flags;
  fp->_flags2 = 0;
  if (stdio_needs_locking)
    fp->_flags2 |= _IO_FLAGS2_NEED_LOCK;
  fp->_IO_buf_base = NULL;
  fp->_IO_buf_end = NULL;
  fp->_IO_read_base = NULL;
  fp->_IO_read_ptr = NULL;
  fp->_IO_read_end = NULL;
  fp->_IO_write_base = NULL;
  fp->_IO_write_ptr = NULL;
  fp->_IO_write_end = NULL;
  fp->_chain = NULL; /* Not necessary. */

  fp->_IO_save_base = NULL;
  fp->_IO_backup_base = NULL;
  fp->_IO_save_end = NULL;
  fp->_markers = NULL;
  fp->_cur_column = 0;
#if _IO_JUMPS_OFFSET
  fp->_vtable_offset = 0;
#endif
#ifdef _IO_MTSAFE_IO
  if (fp->_lock != NULL)
    _IO_lock_init (*fp->_lock);
#endif
}

_IO_FILE 구조체(fp)의 _flags 멤버를 설정해주고 조건에 따라 fp를 null로 초기화하는 것이 주요 기능인가보다.

다음으로 _IO_FILE 구조체(fp)의 _mode 멤버를 설정해주고 0보다 크면 null로 초기화를 진행한다.

 

2. _IO_JUMPS

  _IO_JUMPS (&new_f->fp) = &_IO_file_jumps;

_IO_JUMPS는 libioP.h에 정의된 매크로 함수이다.

#define _IO_JUMPS(THIS) (THIS)->vtable

_IO_file_jumps는 fileop.c에 정의되어 있다.

const struct _IO_jump_t _IO_file_jumps libio_vtable =
{
  JUMP_INIT_DUMMY,
  JUMP_INIT(finish, _IO_file_finish),
  JUMP_INIT(overflow, _IO_file_overflow),
  JUMP_INIT(underflow, _IO_file_underflow),
  JUMP_INIT(uflow, _IO_default_uflow),
  JUMP_INIT(pbackfail, _IO_default_pbackfail),
  JUMP_INIT(xsputn, _IO_file_xsputn),
  JUMP_INIT(xsgetn, _IO_file_xsgetn),
  JUMP_INIT(seekoff, _IO_new_file_seekoff),
  JUMP_INIT(seekpos, _IO_default_seekpos),
  JUMP_INIT(setbuf, _IO_new_file_setbuf),
  JUMP_INIT(sync, _IO_new_file_sync),
  JUMP_INIT(doallocate, _IO_file_doallocate),
  JUMP_INIT(read, _IO_file_read),
  JUMP_INIT(write, _IO_new_file_write),
  JUMP_INIT(seek, _IO_file_seek),
  JUMP_INIT(close, _IO_file_close),
  JUMP_INIT(stat, _IO_file_stat),
  JUMP_INIT(showmanyc, _IO_default_showmanyc),
  JUMP_INIT(imbue, _IO_default_imbue)
};

결론적으로 _IO_JUMPS 함수를 통해 fp에 vtable를 매핑한다.

 

3. _IO_new_file_init_internal

  _IO_new_file_init_internal (&new_f->fp);

_IO_new_file_init_internal은 fileops.c에 정의되어 있다.

void _IO_new_file_init_internal (struct _IO_FILE_plus *fp) {
  /* POSIX.1 allows another file handle to be used to change the position
     of our file descriptor.  Hence we actually don't know the actual
     position before we do the first fseek (and until a following fflush). */
  fp->file._offset = _IO_pos_BAD;
  fp->file._IO_file_flags |= CLOSED_FILEBUF_FLAGS;

  _IO_link_in (fp);
  fp->file._fileno = -1;
}

(뭔가 처리를 하나보다.. 중요해 보이진 않으니 나중에 기회가 되면 알아보자)

 

 

이후 내용들을 살펴보자.

...
#if  !_IO_UNIFIED_JUMPTABLES
  new_f->fp.vtable = NULL;
#endif

  if (_IO_file_fopen ((_IO_FILE *) new_f, filename, mode, is32) != NULL)
    return __fopen_maybe_mmap (&new_f->fp.file);

  _IO_un_link (&new_f->fp);
  free (new_f);
  return NULL;
}

!_IO_UNIFIED_JUMPTABLES가 true이면 new_f->fp.vtable는 NULL이 된다.

_IO_file_fopen ((_IO_FILE *) new_f, filename, mode, is32)의 결과가 NULL이 아니면__fopen_maybe_mmap 함수를 호출한다. NULL이면 free함수를 수행한 뒤 종료된다.

 

_IO_file_fopen은 _IO_new_file_fopen의 별칭이며 정의는 fileops.c에 있다. 

_IO_FILE *_IO_new_file_fopen (_IO_FILE *fp, const char *filename, const char *mode,
		    int is32not64)
{
  int oflags = 0, omode;
  int read_write;
  int oprot = 0666;
  int i;
  _IO_FILE *result;
  const char *cs;
  const char *last_recognized;

  if (_IO_file_is_open (fp))
    return 0;
  switch (*mode)
    {
    case 'r':
      omode = O_RDONLY;
      read_write = _IO_NO_WRITES;
      break;
    case 'w':
      omode = O_WRONLY;
      oflags = O_CREAT|O_TRUNC;
      read_write = _IO_NO_READS;
      break;
    case 'a':
      omode = O_WRONLY;
      oflags = O_CREAT|O_APPEND;
      read_write = _IO_NO_READS|_IO_IS_APPENDING;
      break;
    default:
      __set_errno (EINVAL);
      return NULL;
    }
  ...
}

libc_hidden_ver (_IO_new_file_fopen, _IO_file_fopen)

fopen 함수의 mode 변수가 ‘r', ‘w’, 'a' 문자인지를 확인하고 각 권한에 해당하는 비트가 할당한다.
read_write 변수에 비트가 할당될 때, omode 변수에도 O_RDONLY, O_WRONLY 등의 값이 저장되는 것을 볼 수 있다.

fopen 함수는 결국 open 시스템 콜을 호출해 파일을 열게되는데, 이때 해당 시스템 콜의 인자로 전달된다.

 

_IO_FILE_plus 구조체는 libioP.h에 정의되어 있다.

struct _IO_jump_t {
    JUMP_FIELD(size_t, __dummy);
    JUMP_FIELD(size_t, __dummy2);
    JUMP_FIELD(_IO_finish_t, __finish);
    JUMP_FIELD(_IO_overflow_t, __overflow);
    JUMP_FIELD(_IO_underflow_t, __underflow);
    JUMP_FIELD(_IO_underflow_t, __uflow);
    JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
    /* showmany */
    JUMP_FIELD(_IO_xsputn_t, __xsputn);
    JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
    JUMP_FIELD(_IO_seekoff_t, __seekoff);
    JUMP_FIELD(_IO_seekpos_t, __seekpos);
    JUMP_FIELD(_IO_setbuf_t, __setbuf);
    JUMP_FIELD(_IO_sync_t, __sync);
    JUMP_FIELD(_IO_doallocate_t, __doallocate);
    JUMP_FIELD(_IO_read_t, __read);
    JUMP_FIELD(_IO_write_t, __write);
    JUMP_FIELD(_IO_seek_t, __seek);
    JUMP_FIELD(_IO_close_t, __close);
    JUMP_FIELD(_IO_stat_t, __stat);
    JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
    JUMP_FIELD(_IO_imbue_t, __imbue);
#if 0
    get_column;
    set_column;
#endif
};

/* 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. */

struct _IO_FILE_plus {
  _IO_FILE file;
  const struct _IO_jump_t *vtable;
};

 


__fopen_maybe_mmap

위에서 살펴봤듯이 파일이 잘 열리면 __fopen_maybe_mmap로 가고 아니면 _IO_un_link로 간다.

__fopen_maybe_mmap에 대해 알아보자.

_IO_FILE * __fopen_maybe_mmap (_IO_FILE *fp) {
#ifdef _G_HAVE_MMAP
  if ((fp->_flags2 & _IO_FLAGS2_MMAP) && (fp->_flags & _IO_NO_WRITES))
    {
      /* Since this is read-only, we might be able to mmap the contents
	 directly.  We delay the decision until the first read attempt by
	 giving it a jump table containing functions that choose mmap or
	 vanilla file operations and reset the jump table accordingly.  */

      if (fp->_mode <= 0)
	_IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps_maybe_mmap;
      else
	_IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps_maybe_mmap;
      fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_maybe_mmap;
    }
#endif
  return fp;
}

_G_HAVE_MMAP가 정의되어 있으면 아래 내용을 처리한다.

마지막에 _IO_FILE_plus 구조체인 fp를 반환한다는 점이 중요한 것 같다.

 

참고로 _IO_un_link는 genops.c에 정의되어 있다.

void _IO_un_link (struct _IO_FILE_plus *fp)
{
  if (fp->file._flags & _IO_LINKED)
    {
      struct _IO_FILE **f;
#ifdef _IO_MTSAFE_IO
      _IO_cleanup_region_start_noarg (flush_cleanup);
      _IO_lock_lock (list_all_lock);
      run_fp = (_IO_FILE *) fp;
      _IO_flockfile ((_IO_FILE *) fp);
#endif
      if (_IO_list_all == NULL)
	;
      else if (fp == _IO_list_all)
	_IO_list_all = (struct _IO_FILE_plus *) _IO_list_all->file._chain;
      else
	for (f = &_IO_list_all->file._chain; *f; f = &(*f)->_chain)
	  if (*f == (_IO_FILE *) fp)
	    {
	      *f = fp->file._chain;
	      break;
	    }
      fp->file._flags &= ~_IO_LINKED;
#ifdef _IO_MTSAFE_IO
      _IO_funlockfile ((_IO_FILE *) fp);
      run_fp = NULL;
      _IO_lock_unlock (list_all_lock);
      _IO_cleanup_region_end (0);
#endif
    }
}

_IO_un_link와 이후 호출되는 free로 호출하는 걸 보면 _IO_FILE_plus 구조체인 fp를 해제하는 역할을 하나보다.

 

 

[결론]

fopen을 호출하면 fopen의 본명인 _IO_new_fopen 함수가 실행되고  __fopen_internal을 호출해서 fp에 대한 기본 정보(_flags, _mode 등)를 세팅한다. _IO_new_file_fopen 함수를 통해 잘 열리면  __fopen_maybe_mmap을 호출해 _IO_FILE_plus 구조체인 fp를 반환하고 아니면 _IO_un_link를 통해 힙에서 해제된다.

 


레퍼런스

https://dreamhack.io/lecture/courses/271

 

Background: _IO_FILE

이번 코스에서는 _IO_FILE 구조에 대해 소개합니다.

dreamhack.io

https://aidencom.tistory.com/187

 

[ Linux File Stream ] _IO_FILE & _IO_FILE_plus

리눅스 표준 라이브러리에서 파일 스트림을 나타내기 위한 _IO_FILE 구조체에 대해서 정리해보자. 이에 대한 내용은 Dreamhack Lecture를 참고했다. 또한, 예전에 썼던 내용을 갈아엎은내용이다. ( 예전

41d3n.xyz

 

'background > linux' 카테고리의 다른 글

[glibc-2.27] fwrite & fputs  (1) 2023.01.11
[glibc-2.27] fread & fgets  (0) 2023.01.09
SigReturn-Oriented Programming(SROP)  (0) 2022.12.30
Master Canary  (0) 2022.12.08
SECCOMP  (0) 2022.12.01
    'background/linux' 카테고리의 다른 글
    • [glibc-2.27] fwrite & fputs
    • [glibc-2.27] fread & fgets
    • SigReturn-Oriented Programming(SROP)
    • Master Canary
    ssongk
    ssongk
    벌레 사냥꾼이 되고 싶어요

    티스토리툴바