# 시스템 호출 번호를 검증한 뒤 함수로 연결합니다

v6.18.37 / arch/arm64/kernel/syscall.c

사용자 프로그램은 커널 함수의 주소를 직접 골라 호출하지 않습니다. 시스템 호출 번호를 전달하고 커널은 범위를 확인해 테이블의 함수를 선택합니다. invoke_syscall은 이미 보존된 pt_regs를 받아 호출과 반환값 기록을 연결하는 부분입니다.

## invoke_syscall

```c

static void invoke_syscall(struct pt_regs *regs, unsigned int scno,
			   unsigned int sc_nr,
			   const syscall_fn_t syscall_table[])
{
	long ret;

	add_random_kstack_offset();

	if (likely(scno < sc_nr)) {
		syscall_fn_t syscall_fn;
		syscall_fn = syscall_table[array_index_nospec(scno, sc_nr)];
		ret = __invoke_syscall(regs, syscall_fn);
	} else {
		ret = do_ni_syscall(regs, scno);
	}

	syscall_set_return_value(current, regs, 0, ret);

	/*
	 * This value will get limited by KSTACK_OFFSET_MAX(), which is 10
	 * bits. The actual entropy will be further reduced by the compiler
	 * when applying stack alignment constraints: the AAPCS mandates a
	 * 16-byte aligned SP at function boundaries, which will remove the
	 * 4 low bits from any entropy chosen here.
	 *
	 * The resulting 6 bits of entropy is seen in SP[9:4].
	 */
	choose_random_kstack_offset(get_random_u16());
}

```

### 38행

```c

static void invoke_syscall(struct pt_regs *regs, unsigned int scno,

```

저장된 사용자 레지스터 문맥 regs와 시스템 호출 번호 scno를 받습니다. 반환형은 void이며 호출 결과는 아래에서 regs에 기록합니다.

### 39행

```c

			   unsigned int sc_nr,

```

선택한 시스템 호출 테이블의 유효 항목 수를 받는 함수 인자입니다. 함수 안에서 새로 만드는 지역변수가 아닙니다.

### 40행

```c

			   const syscall_fn_t syscall_table[])

```

호출 함수 포인터들의 테이블을 받는 인자입니다. native·compat 중 어떤 테이블을 넘길지는 상위 경로에서 결정합니다.

### 42행

```c

	long ret;

```

선택한 시스템 호출이 반환한 값이나 음수 오류 코드를 잠시 보관할 지역변수입니다.

### 44행

```c

	add_random_kstack_offset();

```

커널 스택 사용 위치에 무작위 오프셋을 반영하여 예측 가능한 배치를 줄입니다.

### 46행

```c

	if (likely(scno < sc_nr)) {

```

시스템 호출 번호가 테이블 범위 안에 있는지 검사합니다. likely는 흔한 경로라는 최적화 힌트이며 검사를 생략하지 않습니다.

### 47행

```c

		syscall_fn_t syscall_fn;

```

범위 검사 뒤 테이블에서 꺼낼 함수 포인터를 보관합니다. 새 함수를 생성하는 선언이 아닙니다.

### 48행

```c

		syscall_fn = syscall_table[array_index_nospec(scno, sc_nr)];

```

범위 검사한 번호를 추측 실행에서도 제한한 인덱스로 바꿔 함수 포인터를 읽습니다. 테이블의 함수 주소를 고르는 단계이며 아직 시스템 호출 본문은 실행하지 않았습니다.

### 49행

```c

		ret = __invoke_syscall(regs, syscall_fn);

```

선택한 함수에 저장된 레지스터 문맥을 전달하여 실행하고 반환값을 ret에 받습니다. syscall wrapper가 이 문맥에서 실제 인자를 꺼내는 경로로 이어집니다.

### 50행

```c

	} else {

```

앞 조건이 성립하지 않은 경로를 선택합니다. 앞쪽 처리와 아래 처리를 모두 수행하는 것이 아닙니다.

### 51행

```c

		ret = do_ni_syscall(regs, scno);

```

테이블 범위를 벗어난 시스템 호출 번호에 대해 미구현 또는 호환 처리를 수행합니다.

### 54행

```c

	syscall_set_return_value(current, regs, 0, ret);

```

호출 결과를 사용자 복귀에 사용할 레지스터 문맥에 기록합니다. 별도의 error 인자는 0이고 ret에 결과 또는 음수 오류값이 이미 들어 있습니다.

### 65행

```c

	choose_random_kstack_offset(get_random_u16());

```

난수로 다음 진입에서 사용할 스택 오프셋을 갱신합니다. 이 줄의 난수값이 그대로 무제한 스택 크기가 된다고 해석하지 말고 오프셋 매크로의 제한·정렬 처리와 함께 읽으셔야 합니다.

