뭔가 fclose니까 할당 해제, 초기화와 관련된 것들만 다 해주고 끝날 것 같다.
자세히 알아보자.
# define fclose(fp) _IO_new_fclose (fp)
int
_IO_new_fclose (FILE *fp)
{
int status;
CHECK_FILE(fp, EOF);
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
/* We desperately try to help programs which are using streams in a
strange way and mix old and new functions. Detect old streams
here. */
if (_IO_vtable_offset (fp) != 0)
return _IO_old_fclose (fp);
#endif
/* First unlink the stream. */
if (fp->_flags & _IO_IS_FILEBUF)
_IO_un_link ((struct _IO_FILE_plus *) fp);
_IO_acquire_lock (fp);
if (fp->_flags & _IO_IS_FILEBUF)
status = _IO_file_close_it (fp);
else
status = fp->_flags & _IO_ERR_SEEN ? -1 : 0;
_IO_release_lock (fp);
_IO_FINISH (fp);
if (fp->_mode > 0)
{
/* This stream has a wide orientation. This means we have to free
the conversion functions. */
struct _IO_codecvt *cc = fp->_codecvt;
__libc_lock_lock (__gconv_lock);
__gconv_release_step (cc->__cd_in.step);
__gconv_release_step (cc->__cd_out.step);
__libc_lock_unlock (__gconv_lock);
}
else
{
if (_IO_have_backup (fp))
_IO_free_backup_area (fp);
}
_IO_deallocate_file (fp);
return status;
}
당연히 그냥 불러올 리가 없다. 매크로에 의해 _IO_new_fclose
를 호출하게 된다.
_IO_IS_FILEBUF
가 설정되어 있다면 _IO_un_link
를 하게 된다.
또한, _IO_file_close_it
를 호출해서 내부적으로 close
함수를 호출해서 파일을 닫아준다.
마지막으로, _IO_FINISH
를 해 주고, _IO_deallocate_file
을 해 준다.
_IO_IS_FILEBUF
매크로가 무엇을 뜻하는지는 잘 모르겠다. 왜 얘가 있을때 unlink, close를 해 주는 거지..?
내부적으로 호출해주는 세 함수에 대해 알아보자.
_IO_un_link
void
_IO_un_link (struct _IO_FILE_plus *fp)
{
if (fp->file._flags & _IO_LINKED)
{
FILE **f;
#ifdef _IO_MTSAFE_IO
_IO_cleanup_region_start_noarg (flush_cleanup);
_IO_lock_lock (list_all_lock);
run_fp = (FILE *) fp;
_IO_flockfile ((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 == (FILE *) fp)
{
*f = fp->file._chain;
break;
}
fp->file._flags &= ~_IO_LINKED;
#ifdef _IO_MTSAFE_IO
_IO_funlockfile ((FILE *) fp);
run_fp = NULL;
_IO_lock_unlock (list_all_lock);
_IO_cleanup_region_end (0);
#endif
}
}
libc_hidden_def (_IO_un_link)
별 건 없다. 말 그대로 unlink해주는 함수이다.
전역변수 _IO_list_all
에서 순회하면서 해당하면 unlink 시켜주고, 플래그도 해제해준다.
_IO_file_close_it
int
_IO_new_file_close_it (FILE *fp)
{
int write_status;
if (!_IO_file_is_open (fp))
return EOF;
if ((fp->_flags & _IO_NO_WRITES) == 0
&& (fp->_flags & _IO_CURRENTLY_PUTTING) != 0)
write_status = _IO_do_flush (fp);
else
write_status = 0;
_IO_unsave_markers (fp);
int close_status = ((fp->_flags2 & _IO_FLAGS2_NOCLOSE) == 0
? _IO_SYSCLOSE (fp) : 0);
/* Free buffer. */
if (fp->_mode > 0)
{
if (_IO_have_wbackup (fp))
_IO_free_wbackup_area (fp);
_IO_wsetb (fp, NULL, NULL, 0);
_IO_wsetg (fp, NULL, NULL, NULL);
_IO_wsetp (fp, NULL, NULL);
}
_IO_setb (fp, NULL, NULL, 0);
_IO_setg (fp, NULL, NULL, NULL);
_IO_setp (fp, NULL, NULL);
_IO_un_link ((struct _IO_FILE_plus *) fp);
fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
fp->_fileno = -1;
fp->_offset = _IO_pos_BAD;
return close_status ? close_status : write_status;
}
libc_hidden_ver (_IO_new_file_close_it, _IO_file_close_it)
만약 write 모드로 열렸고, 버퍼에 쓴 내용이 남아있는 경우, _IO_do_flush
를 통해서 남은 내용을 write 해 준다.
그 후 _IO_SYSCLOSE
를 통해 close 해준다.
마지막으로 _IO_setb
함수에서 버퍼를 free 해주고, unlink까지 해 준 후 함수는 종료된다.
_IO_FINISH
#define _IO_FINISH(FP) JUMP1 (__finish, FP, 0)
void
_IO_new_file_finish (FILE *fp, int dummy)
{
if (_IO_file_is_open (fp))
{
_IO_do_flush (fp);
if (!(fp->_flags & _IO_DELETE_DONT_CLOSE))
_IO_SYSCLOSE (fp);
}
_IO_default_finish (fp, 0);
}
libc_hidden_ver (_IO_new_file_finish, _IO_file_finish)
자꾸 이런저런 함수들을 호출하는데, 결국 제대로 닫혔는지 이중, 삼중으로 확인하는 것 같다. 결국 큰 흐름은 _IO_new_fclose
를 통해 이루어진다고 보면 될 것 같다.
다음에는 지금까지 알아본 내용들을 바탕으로 어떻게 exploit에 이용할 수 있을 지 알아보자.