get_fs 와 set_fs

[출처] fromdj.pe.kr

리눅스의 사용자 프로세스는 0~3기가 까지의 가상메모리를 사용한다.

그리고 그위 1G 는 커널의 영역이다. 리눅스는 기본적으로 시스템 콜을 호출하는 주체가 사용자 프로세스 라고 염두하고 있고, 사실
커널내에서 시스템 콜을 사용하지 못하게(어렵게) 되어 있다. 

리눅스 커널은 시스템 콜을 호출할때, 사용자의 인자로 넘어온 메모리 포인터가 가상메모리 주소 0~3기가 내의 부분인지를 검사하고 그렇지 않으면 부적절 하다고 판단한다. 

그런데 특별한 상황을 생각해 보자. 모듈내에서 시스템 콜을 사용하고자 할때 시스템콜의 주체는 커널이며 넘어가는 인자도 커널의 메모리 영역이다. 그래서 리눅스 시스템콜은 녀석이 부적절한 메모리라고 생각하게 된다. (커널의 메모리 주소는 3기가 이상일 것이므로) 

커널이 검사하는 부분은 해당하는 메모리의 주소가 KERNEL_DS 아래의 녀석인가를 검사한다. 즉 그것은 그 메모리가 사용자 영역의 메모리 임을 검사하는 것이다. 그것의 기준이 current->addr_limit에 저장된다. 그 부분을 4G까지 올리면 ㅁ커널 메모리 영역의 메모리 포인터에 대해서도 오류라고 생각하지 않는다. 

이것을 위해 필요한 매크로 (함수가 아니다) 를 보자. (arch/i386/uaccess.h 에 정의)

#define get_ds()        (KERNEL_DS) /* it’s 0xFFFFFFFF */
#define get_fs()        (current->addr_limit)
#define set_fs(x)        (current->addr_limit = (x))

이 매크로는 현재 메모리 세그먼트 정보 구조체에서 메모리의 한계를 얻어오고, 변경하는 일을 한다.
즉 이 set_fs를 통해서 0~3 기가의 영역으로 되어 있는 부분을 4기가까지 늘려잡고 시스템콜을 호출한 뒤에 다시 돌려 놓으면 된다.

get_ds 는 커널의 DS (data segment) 를 가져오는 메크로 이다.
다음의 코드가 예가 될것이다.

      mm_segment_t fs;

      fs = get_fs();     /* save previous value */
      set_fs (get_ds()); /* use kernel limit */

      /* system calls can be invoked */

      set_fs(fs); /* restore before returning to user space */

Leave a Reply