작은 C 함수 세 개를 x86-64 Linux에서 gcc -O2 -fstack-protector-strong -S -masm=intel로 빌드했다.

#include <stdio.h>

int read_line(FILE *f) {
    char buf[64];
    return fgets(buf, sizeof buf, f) != NULL;
}

int read_count(void) {
    int n = 0;
    return scanf("%d", &n) == 1 ? n : -1;
}

int add(int a, int b) { return a + b; }

read_line과 read_count는 같은 프롤로그를 받는다. mov rax, QWORD PTR fs:40이 가드를 읽는다. 다음 저장이 그 값을 프레임에 둔다. 이어서 xor eax, eax가 레지스터를 지워 가드가 남지 않게 한다. ret 전에 저장한 사본을 다시 읽어 fs:40과 비교한다. 여기서는 sub, 예전 리스트에서는 xor다. 검사가 실패하면 __stack_chk_fail을 호출하는 블록으로 뛴다. objdump는 같은 피연산자를 %fs:0x28로 찍는다. 오프셋은 같고 한쪽은 10진수, 한쪽은 16진수다.

add에는 아무것도 없다. 배열도 없고 주소를 꺼낸 지역 변수도 없다.

그냥 -fstack-protector로 바꾸면 read_line은 카나리를 유지하고 read_count는 잃는다. GCC 매뉴얼에 이유가 있다. strong은 지역 배열이 있거나 지역 프레임 주소를 참조하는 함수를 더하고, &n이 바로 그것이다.

fs:40은 glibc가 스레드 제어 블록에 가드를 두는 자리다. 그건 호출 규약이 아니다. 배포판 컴파일러 중에는 strong을 기본으로 켜는 것도 있으니 기준 리스트에는 -fno-stack-protector가 필요하다.