# Stack unwind, function tracing과 return address 보호

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

스택 추적은 메모리에서 주소처럼 보이는 값을 아무거나 모으는 작업이 아닙니다. arm64는 frame record와 별도 메타데이터, x86은 ORC 등의 unwind 정보, RISC-V는 설정에 따른 frame 규칙을 이용합니다. 대표로 arm64가 다음 frame record를 검증하며 걷는 함수를 읽습니다.

## kunwind_next_frame_record

```c

static __always_inline int
kunwind_next_frame_record(struct kunwind_state *state)
{
	unsigned long fp = state->common.fp;
	struct frame_record *record;
	struct stack_info *info;
	unsigned long new_fp, new_pc;

	if (fp & 0x7)
		return -EINVAL;

	info = unwind_find_stack(&state->common, fp, sizeof(*record));
	if (!info)
		return -EINVAL;

	record = (struct frame_record *)fp;
	new_fp = READ_ONCE(record->fp);
	new_pc = READ_ONCE(record->lr);

	if (!new_fp && !new_pc)
		return kunwind_next_frame_record_meta(state);

	unwind_consume_stack(&state->common, info, fp, sizeof(*record));

	state->common.fp = new_fp;
	state->common.pc = new_pc;
	state->source = KUNWIND_SOURCE_FRAME;

	return 0;
}

```

### 216행

```c

static __always_inline int

```

호출 지점에 인라인될 수 있는 내부 helper이며 성공 또는 오류를 int로 반환합니다.

### 217행

```c

kunwind_next_frame_record(struct kunwind_state *state)

```

현재 추적 위치와 스택 사용 상태가 들어 있는 state를 받아 다음 프레임으로 진행합니다.

### 219행

```c

	unsigned long fp = state->common.fp;

```

지금 프레임을 찾을 FP 값을 지역 변수로 꺼냅니다. 이 주소를 바로 역참조하지 않고 먼저 검사합니다.

### 220행

```c

	struct frame_record *record;

```

검증 후 사용할 frame_record 포인터를 선언합니다.

### 221행

```c

	struct stack_info *info;

```

이 주소가 들어 있는 유효한 스택 영역 설명을 받을 포인터입니다.

### 222행

```c

	unsigned long new_fp, new_pc;

```

다음 FP와 PC를 따로 보관합니다. 검증을 마치기 전에 현재 추적 상태를 덮어쓰지 않도록 합니다.

### 224행

```c

	if (fp & 0x7)

```

FP의 하위 3비트가 0인지 확인하여 이 레코드 접근의 8바이트 정렬 조건을 검사합니다.

### 225행

```c

		return -EINVAL;

```

정렬이 잘못된 주소이면 추적을 계속하지 않고 잘못된 입력 오류를 반환합니다.

### 227행

```c

	info = unwind_find_stack(&state->common, fp, sizeof(*record));

```

FP부터 frame_record 전체 크기까지 들어가는 유효한 스택 영역을 찾습니다. 시작 주소만 범위 안이라고 충분하지 않습니다.

### 228행

```c

	if (!info)

```

해당 범위를 담는 스택을 찾지 못했는지 확인합니다.

### 229행

```c

		return -EINVAL;

```

스택 밖의 임의 메모리를 프레임으로 읽지 않도록 오류로 끝냅니다.

### 231행

```c

	record = (struct frame_record *)fp;

```

검사가 끝난 FP를 프레임 기록 포인터로 해석합니다.

### 232행

```c

	new_fp = READ_ONCE(record->fp);

```

레코드에 저장된 이전 FP를 한 번 읽습니다. READ_ONCE는 컴파일러가 접근을 임의로 합치거나 다시 생성하는 일을 제한합니다.

### 233행

```c

	new_pc = READ_ONCE(record->lr);

```

저장된 LR을 다음 PC 값으로 읽습니다. FP와 LR은 각각 연결 정보와 복귀 위치입니다.

### 235행

```c

	if (!new_fp && !new_pc)

```

두 값이 모두 0인 특수 레코드인지 확인합니다.

### 236행

```c

		return kunwind_next_frame_record_meta(state);

```

그 경우 일반 연결을 따르지 않고 별도 프레임 메타데이터 경로로 넘깁니다.

### 238행

```c

	unwind_consume_stack(&state->common, info, fp, sizeof(*record));

```

이미 사용한 프레임의 스택 범위를 소비 처리하여 이후 걷기가 같은 영역을 부적절하게 다시 사용하지 않도록 합니다.

### 240행

```c

	state->common.fp = new_fp;

```

다음 단계가 사용할 FP를 갱신합니다.

### 241행

```c

	state->common.pc = new_pc;

```

이번에 찾은 호출자의 복귀 위치를 추적 PC로 기록합니다.

### 242행

```c

	state->source = KUNWIND_SOURCE_FRAME;

```

이 결과가 frame record에서 얻어진 것임을 표시합니다. 다른 메타데이터 출처와 구분하는 정보입니다.

### 244행

```c

	return 0;

```

다음 프레임으로 한 단계 진행했음을 성공으로 반환합니다.

