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

v6.6 / 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 (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);

	/*
	 * Ultimately, this value will get limited by KSTACK_OFFSET_MAX(),
	 * but not enough for arm64 stack utilization comfort. To keep
	 * reasonable stack head room, reduce the maximum offset to 9 bits.
	 *
	 * The actual entropy will be further reduced by the compiler when
	 * applying stack alignment constraints: the AAPCS mandates a
	 * 16-byte (i.e. 4-bit) aligned SP at function boundaries.
	 *
	 * The resulting 5 bits of entropy is seen in SP[8:4].
	 */
	choose_random_kstack_offset(get_random_u16() & 0x1FF);
}

```

### 40행

```c

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

```

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

### 41행

```c

			   unsigned int sc_nr,

```

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

### 42행

```c

			   const syscall_fn_t syscall_table[])

```

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

### 44행

```c

	long ret;

```

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

### 46행

```c

	add_random_kstack_offset();

```

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

### 48행

```c

	if (scno < sc_nr) {

```

시스템 호출 번호가 테이블의 유효 범위 안에 있는지 검사합니다. 범위를 벗어나면 미구현 처리 경로로 갑니다.

### 49행

```c

		syscall_fn_t syscall_fn;

```

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

### 50행

```c

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

```

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

### 51행

```c

		ret = __invoke_syscall(regs, syscall_fn);

```

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

### 52행

```c

	} else {

```

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

### 53행

```c

		ret = do_ni_syscall(regs, scno);

```

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

### 56행

```c

	syscall_set_return_value(current, regs, 0, ret);

```

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

### 69행

```c

	choose_random_kstack_offset(get_random_u16() & 0x1FF);

```

v6.6의 이 줄은 난수의 하위 9비트를 남겨 다음 스택 오프셋 선택에 사용합니다. 실제 스택 조정에서는 정렬 등 별도 제약도 적용됩니다.

