먼저 이해할 두 가지
숫자 하나씩 계산하는 대신 같은 종류의 숫자 여러 개를 한꺼번에 처리한다. 사진의 pixel, audio sample, AI tensor와 과학 계산에서 같은 명령을 반복하는 비용을 줄인다.
프로그램이 잘못된 memory를 읽거나 공격자가 함수의 돌아갈 주소를 바꾸는 순간을 hardware가 검사한다. 버그를 없애는 마법이 아니라, 버그가 공격으로 이어지기 어렵게 만드는 여러 겹의 검사다.
ARMv9의 특징은 이 둘을 한 architecture 안에서 확장했다는 점이다. SVE2·SME는 더 많은 데이터를 효율적으로 처리하고, MTE·PAC·BTI·GCS는 그 큰 software가 memory와 실행 흐름을 잘못 다룰 때 피해를 줄인다.
SVE와 SVE2의 차이
SVE는 128~2048 bit의 구현 선택 vector length를 같은 binary가 활용하도록 vector-length-agnostic(VLA) programming model을 도입했다. SVE2는 그 predication과 scalable register model을 유지하면서 DSP·multimedia·computer vision·암호 등 정수 중심 workload에 필요한 연산을 크게 넓힌다.
| 기능 | 핵심 목적 | 명령 수준 차이 | 확인점 |
|---|---|---|---|
| Neon | 고정 128-bit SIMD | lane 수가 compile time에 고정 | 기존 ABI와 library 최적화가 매우 넓다. |
| SVE | HPC와 VLA vectorization | predicate register, while-loop, gather/scatter와 scalable Z register | HWCAP_SVE와 vector length를 검사한다. |
| SVE2 | 범용 DSP·media·crypto 확대 | narrowing, widening, complex integer, bit permutation과 domain-specific 연산 확대 | HWCAP2_SVE2 및 세부 extension을 확인한다. |
| SVE2.1+ | 명령군과 predication의 연차 개선 | 새 load/store·permutation·arithmetic 기능이 추가됨 | architecture revision만 보지 말고 개별 FEAT_*를 본다. |
VLA 규칙: loop가 처리할 element 수를 svcnt*로 얻고, whilelt 계열 predicate로 tail을 처리해야 한다. 256-bit라고 가정해 상수를 넣으면 더 짧거나 긴 CPU에서 이식성이 깨진다.
길이가 10인 배열을 SVE가 처리하는 방법
예를 들어 CPU의 vector length가 256 bit라면 32-bit 정수 8개를 한 번에 담을 수 있다. 원소 10개를 더할 때 첫 회에는 8개, 두 번째 회에는 남은 2개만 활성화한다. 같은 binary가 128-bit CPU에서는 4+4+2개, 512-bit CPU에서는 10개를 한 번에 처리한다.
Predicate가 마지막 2개만 켜는 과정
배열 index: 0 1 2 3 4 5 6 7 | 8 9
첫 반복 predicate: 1 1 1 1 1 1 1 1
둘째 predicate: 1 1 0 0 0 0 0 0
whilelt p0.s, xIndex, xCount // index < count인 lane만 1
ld1w z0.s, p0/z, [xBase, xIndex, lsl #2]
add zAcc.s, p0/m, zAcc.s, z0.s
incw xIndex // 현재 VL에 들어가는 32-bit 원소 수만큼 증가p0/z는 꺼진 lane을 0으로 만들고, p0/m은 꺼진 lane의 기존 목적지 값을 유지한다. 이 차이를 모르면 마지막 반복에서 이전 값이 섞이거나 합계가 틀릴 수 있다.
SVE2는 이 가변 길이·predicate 구조를 유지하면서 saturating arithmetic, widening/narrowing, bit permutation, complex arithmetic와 암호 명령을 보강한다. 따라서 “SVE2는 SVE보다 register가 더 넓다”가 아니라 “같은 scalable 실행 틀로 다룰 수 있는 정수·DSP 작업이 넓어졌다”가 정확하다.
SME는 SVE에 matrix storage를 더한다
Scalable Matrix Extension은 Streaming SVE mode와 2차원 ZA storage를 도입한다. streaming mode의 vector length는 일반 SVE vector length와 독립적으로 관리될 수 있고, 함수가 streaming 상태나 ZA를 공유·보존하는 방식은 호출 ABI에 영향을 준다.
Streaming SVE mode
긴 matrix loop에 맞춘 실행 mode다. mode 전환과 streaming-compatible 함수 속성을 compiler ABI가 추적한다.
Scalable matrix storage
vector length의 제곱에 비례하는 큰 상태다. context switch와 signal frame 비용을 지배할 수 있다.
추가 tile storage
일부 SME2 연산이 사용하는 별도 상태이며 ABI에서 live·preserve 계약을 명시한다.
Full A64 in streaming mode
streaming mode에서 사용할 수 있는 명령 범위를 확장한다. 구현 feature를 별도로 확인한다.
Multi-vector와 outer product
여러 Z register를 묶는 연산과 tile 처리로 matrix kernel의 instruction overhead를 줄인다.
Quarter-tile·sparsity
더 작은 tile 조작과 structured sparsity 등 AI workload의 data movement를 줄이는 방향으로 확장된다.
SME가 matrix 곱을 계산하는 원리
matrix 곱 C = A × B는 A의 column 방향 값과 B의 row 방향 값을 곱해 C의 여러 칸에 누적하는 작업이다. SME는 두 vector의 outer product를 한 번에 계산하고 결과를 2차원 ZA storage에 계속 더한다.
개념적인 SME 실행 순서
SMSTART ZA // Streaming mode와 ZA 접근 시작
ZERO {ZA} // 누적 matrix를 0으로 초기화
loop_k:
load zA, pA, [A] // A의 한 방향 vector
load zB, pB, [B] // B의 한 방향 vector
FMOPA za0.s, pA/m, pB/m, zA.s, zB.s
... // K 방향을 반복하며 같은 ZA tile에 누적
store ZA rows to C
SMSTOP // mode와 ZA 사용 종료실제 instruction과 tile element type은 data 형식에 따라 달라진다. predicate 두 개는 A와 B 양쪽의 유효 원소를 제한해 matrix 가장자리의 불완전한 tile도 같은 loop로 처리한다.
공식 Arm SME 설명에 따르면 ZA는 SVL 바이트 × SVL 바이트의 정사각 byte array다. SVL이 256 bit, 즉 32 byte면 ZA는 32×32 = 1,024 byte다. SVL이 512 bit면 64×64 = 4,096 byte가 된다. vector length가 두 배가 되면 ZA 저장량은 네 배가 되므로 context switch 비용도 빠르게 커진다.
Streaming mode는 PSTATE.SM, ZA 접근은 PSTATE.ZA가 제어한다. SMSTART/SMSTOP은 이 bit를 바꾸는 명령 alias다. mode 전환 시 streaming과 non-streaming vector 상태 규칙이 달라지므로 compiler의 __arm_streaming·ZA 공유 attribute가 함수 경계에서 중요하다.
BF16, I8MM, FP8과 FP6는 연산 계약이 다르다
| 형식·기능 | 주요 용도 | 누산·정확도 관점 |
|---|---|---|
| BF16 | 학습·추론의 넓은 exponent 범위 | 입력 저장량을 줄이고 더 넓은 형식으로 누산하는 사용이 일반적이다. |
| I8MM | 8-bit integer matrix multiply | 작은 정수 입력을 더 넓은 accumulator로 모은다. |
| F32MM / F64MM | SVE matrix multiply | float32·float64 workload의 multiply-accumulate throughput을 높인다. |
| FP8 | AI model의 저정밀 저장·연산 | 여러 FP8 encoding, scaling과 exception 처리 계약을 함께 봐야 한다. |
| FP6 | v9.7 세대의 더 압축된 AI 데이터 | 정밀도·동적 범위 손실을 software quantization 정책과 함께 관리한다. |
“지원한다”는 말은 register가 그 형식을 직접 담는지, 변환 명령이 있는지, dot-product 또는 matrix 명령이 있는지를 구분해야 한다. compiler intrinsic과 library kernel이 같은 encoding·accumulator 규칙을 택하는지도 확인한다.
Vector length가 Linux ABI 크기를 바꾼다
SVE·SME register state는 구현 vector length에 따라 커진다. Linux는 프로세스별 vector length와 기능 opt-in을 관리하고, signal frame·ptrace regset·core dump에 가변 크기 record를 사용한다.
prctl()SVE·SME vector length를 조회·설정하고 exec 이후 상속 정책을 지정한다. 요청값이 그대로 보장된다고 가정하지 말고 반환값을 사용한다.
FPSIMD, SVE, ZA, ZT record를 header와 size로 순회한다. 고정 offset으로 casting하면 새 extension에서 깨진다.
ptrace()debugger는 NT_ARM_SVE·SME 계열 regset의 크기와 flags를 먼저 읽고 상태 형식을 해석한다.
kernel은 사용 여부와 ownership을 추적해 큰 상태를 필요할 때 저장한다. ZA는 VL 제곱에 비례해 특히 비싸다.
구체적인 kernel 저장·복원은 SVE/SME extended state 비교와 Linux SVE ABI, Linux SME ABI에서 이어진다.
Vector length가 상태 저장량을 얼마나 키우는가
Linux 문서에서 VL은 Z register의 크기를 byte로 나타낸다. 각 Z register는 VL byte, 각 predicate와 FFR은 VL/8 byte다. scalable 부분만 단순 계산하면 다음과 같다.
| 상태 | 계산식 | VL=32B (256-bit) | VL=256B (2048-bit) |
|---|---|---|---|
| Z0..Z31 | 32 × VL | 1,024B | 8,192B |
| P0..P15 | 16 × VL/8 | 64B | 512B |
| FFR | VL/8 | 4B | 32B |
| ZA | SVL × SVL | 1,024B | 65,536B |
여기에 FPSIMD state, header, alignment와 ZT 등 추가 record가 붙는다. 그래서 kernel은 모든 task마다 최대 크기를 무조건 복사하지 않고 실제 사용 여부를 추적한다. signal frame은 sve_context.vl과 record size를 포함하고, debugger는 NT_ARM_SVE·NT_ARM_ZA regset의 header를 먼저 읽는다.
실무 오류: signal frame에서 register offset을 고정 숫자로 계산하면 다른 VL이나 SME 추가 state에서 깨진다. Linux UAPI의 SVE_SIG_*·ZA_SIG_* macro와 record header를 사용해야 한다.
ACLE intrinsic과 compile target
ACLE은 C/C++에서 architecture feature와 vector type을 사용하는 표준 interface다. SVE intrinsic은 보통 arm_sve.h를, SME는 compiler가 제공하는 SME attribute와 intrinsic을 사용한다.
Vector length를 고정하지 않는 SVE 합산 골격
#include <arm_sve.h>
float sum_sve(const float *p, size_t n) {
svfloat32_t acc = svdup_f32(0.0f);
for (size_t i = 0; i < n; i += svcntw()) {
svbool_t pg = svwhilelt_b32(i, n);
acc = svadd_m(pg, acc, svld1(pg, p + i));
}
return svaddv(svptrue_b32(), acc);
}
-march=armv9-a+sve2 같은 compile target은 그 명령을 실행할 CPU가 보장될 때 사용한다. 범용 binary는 별도 object 또는 function multiversioning과 HWCAP dispatch를 사용한다.
MTE는 allocation tag와 pointer tag를 비교한다
Memory Tagging Extension은 memory granule에 allocation tag를, pointer 상위 bit에 logical tag를 두고 접근 때 비교한다. use-after-free와 일부 out-of-bounds를 탐지하지만, tag 충돌 확률과 allocator 정책 때문에 완전한 memory safety 증명은 아니다.
| Fault mode | 관찰 시점 | 장단점 |
|---|---|---|
| Synchronous | 문제가 된 접근에서 정확히 fault | debugging과 원인 추적에 좋지만 실행 비용이 더 클 수 있다. |
| Asynchronous | 오류를 기록하고 나중에 전달 | 성능은 유리할 수 있으나 faulting instruction을 정확히 특정하기 어렵다. |
| Asymmetric | load와 store에 서로 다른 정책 적용 가능 | workload별 성능·탐지 절충을 세밀하게 조정한다. |
세대별 MTE 확장은 tag storage·fault control·virtualization을 개선한다. 실제 process는 tagged address ABI, allocator와 prctl() 설정이 모두 맞아야 한다. Linux MTE 문서에 userspace 계약을 연결했다.
MTE가 use-after-free를 찾는 예
MTE는 memory의 16-byte granule마다 4-bit allocation tag를 두고 pointer의 logical tag와 비교한다. 자물쇠와 열쇠 번호가 같은지를 매 load/store 때 확인한다고 생각하면 된다.
해제된 pointer를 다시 사용할 때
1. malloc()이 0x...1000 영역에 allocation tag 5를 지정
2. application pointer의 상위 tag도 5 → 접근 성공
3. free() 후 allocator가 같은 영역을 tag 9로 변경
4. 오래된 pointer는 여전히 tag 5
5. pointer tag 5 != memory tag 9 → Tag Check Fault공간 오류도 pointer가 다른 tag의 16-byte granule로 넘어가면 잡을 수 있다. 다만 우연히 같은 4-bit tag가 재사용되거나 한 granule 안에서 벗어나면 탐지하지 못할 수 있다.
SCTLR_ELx.TCF 계열 설정은 mismatch를 무시할지, 정확한 instruction에서 synchronous fault로 보고할지, 비동기적으로 모아 나중에 알릴지 정한다. 개발 환경은 정확한 위치가 필요한 synchronous mode가 유리하고, 운영 환경은 비용과 진단 정확도를 절충한다.
Arm의 PAC·BTI·MTE 입문 문서는 logical tag가 4-bit이며 mismatch 처리 방식을 system register로 선택한다고 설명한다.
PAC는 pointer와 context를 인증한다
Pointer Authentication은 pointer의 사용되지 않는 상위 bit에 인증 code를 넣고, secret key와 modifier를 사용해 변조를 탐지한다. instruction·data pointer용 key가 분리되며 EL별 key 관리와 context switch가 필요하다.
APIAKey, APIBKey, APDAKey, APDBKey와 generic key는 용도를 분리한다. kernel·hypervisor가 context 전환 때 올바르게 관리한다.
stack pointer, program counter 또는 별도 context 값을 섞어 같은 pointer의 재사용 공격 범위를 줄인다.
implementation과 architecture feature에 따라 QARMA 계열 지원이 진화한다. v9.3의 QARMA3는 지연과 보안 특성의 새 선택지를 제공한다.
인증 실패 pointer는 후속 사용에서 fault하도록 손상된다. PAC 자체가 bounds check나 memory encryption을 제공하지는 않는다.
Linux pointer authentication ABI에서 HWCAP, key와 virtualization 노출을 확인한다.
BTI와 GCS는 서로 다른 제어 흐름을 막는다
| 기능 | 보호 대상 | 방법 | 남는 공격면 |
|---|---|---|---|
| BTI | 간접 branch의 목적지 | 허용된 landing pad에서만 특정 간접 branch가 도착하도록 검사 | 허용된 target 내부의 논리 공격과 return chain은 별도 보호가 필요하다. |
| PAC | pointer와 return address 무결성 | key·modifier 기반 인증 code 검사 | 올바르게 서명된 pointer의 오용과 data corruption은 남는다. |
| GCS | return address 흐름 | 일반 stack과 별도 보호 stack에 return state를 기록·대조 | forward-edge 간접 branch는 BTI 등 별도 보호가 필요하다. |
GCS는 hardware, ELF property, loader, kernel의 prctl()·signal 처리와 compiler가 함께 작동해야 한다. 자세한 ABI는 Linux GCS 문서에서 본다.
함수 반환 주소를 세 겹으로 보호하는 과정
함수 호출 명령 BL은 돌아갈 주소를 LR에 넣는다. 함수가 LR을 stack에 저장한 뒤 buffer overflow가 stack을 덮으면 공격자는 return 주소를 자신이 원하는 code로 바꿀 수 있다.
| 보호 | 함수 호출 때 하는 일 | 공격된 return 때 결과 |
|---|---|---|
| PAC | PACIASP가 LR, instruction key와 현재 SP로 서명을 만들어 pointer 상위 bit에 넣는다. | AUTIASP 인증이 실패해 유효한 return address를 얻지 못한다. |
| GCS | 일반 data stack과 별도의 보호된 Guarded Control Stack에도 예상 return state를 기록한다. | 일반 stack의 return address와 보호 copy가 다르면 예외가 발생한다. |
| BTI | 간접 호출이 도착해도 되는 함수 entry에 BTI c 같은 landing pad를 둔다. | 공격자가 함수 중간 instruction으로 간접 branch하면 landing-pad 검사가 막는다. |
PAC를 사용한 전형적 함수 골격
function:
paciasp // LR을 key IA + SP context로 서명
stp x29, x30, [sp, #-16]!
... // 함수 본문
ldp x29, x30, [sp], #16
autiasp // LR 서명 확인
retmodifier에 SP를 쓰면 다른 stack frame에서 가져온 서명된 return address를 그대로 재사용하기 어렵다. 하지만 올바른 key와 context로 이미 서명된 pointer의 논리적 오용까지 막는 것은 아니다.
Checked Pointer Arithmetic
v9.5 계열의 Checked Pointer Arithmetic은 pointer 계산에서 address space 범위를 벗어나거나 예상하지 못한 wrap이 생기는 것을 검사하도록 돕는다. MTE가 접근 시 tag를 검사하고 PAC가 pointer 무결성을 검사한다면, CPA는 pointer를 만드는 산술 단계의 오류를 겨냥한다.
서로 대체하지 않는다: CPA, MTE, PAC, BTI와 GCS는 각각 arithmetic, memory temporal/spatial bug 탐지, pointer 인증, indirect branch, return 흐름을 담당한다. threat model에 맞춰 compiler·OS·loader까지 함께 켜야 한다.
실제 Linux에서 확인할 것
getauxval(AT_HWCAP)과AT_HWCAP2로 SVE2·SME·MTE·PAC·BTI·GCS 세부 bit를 확인한다.prctl()반환값으로 SVE/SME VL과 MTE fault mode, GCS process policy를 확인한다.- signal handler와 debugger가 가변 크기 context record를 안전하게 순회하는지 검사한다.
- compile option만 보고 배포하지 말고 illegal instruction fallback과 runtime dispatch를 시험한다.
dmesg에서 feature 비활성화, 이질 CPU 제약과 erratum workaround를 확인한다.
Linux가 큰 CPU 상태를 관리하는 방법
명령 설명에서 멈추지 않고 task 전환, signal frame, vector length 설정과 MTE fault 처리로 이어진다.
공식 자료
- SVEIntroduction to SVE
- SVE2Introduction to SVE2
- SMEScalable Matrix Extension for Armv9-A
- SME programmingArm Scalable Matrix Extension introduction
- MTE·PAC·BTIProviding protection for complex software
- MTE userspaceArm Memory Tagging Extension user guide
- ACLEArm C Language Extensions
- ABIArm ABI specifications
- ArchitectureDDI 0487: A-profile Architecture Reference Manual