지난 포스팅에서는 이론적인 측면에서 어떻게 memory safety를 다루는지를 보았다. 여기서는 Java와 Rust를 통해 고급 언어들에서 실질적으로 어떻게 memory safety를 보장하는지 알아보자.

 

[1] Java의 경우 : bound check + garbage collection

bound check는 명확하다. array 등의 원소에 접근할 때 bound 내의 접근인지를 검사하는 것이다. 이전 포스팅에서 본 Softbound와 근본적으로는 유사한 방법을 사용한다. 

 

핵심은 Gabage collection(GC)이다. GC는 메모리 할당/해제의 추상 레벨을 하나 더 둠으로써 memory safety를 보장한다. 즉, 프로그래머가 직접 메모리를 할당/해제하는 것이 아니라, 요청을 하면 런타임에 GC가 알아서 이를 해제해준다. 물론 퍼포먼스 오버헤드가 발생하게 된다. 하지만 해제된 메모리들을 GC가 알아서 수집하기 때문에, 보안 관점에서 보면 이보다 깔끔한 해결책이 없다. dangling pointer나 use-after-free같은 디버깅하기 어려운 메모리 취약점들에 신경 쓸 필요가 없어지기 때문이다. 

 

Java의 이런 해결책은 memory safety를 보장하는 많은 고급 프로그래밍 언어에서 사용하는 전통적인 방식이라고 할 수 있다.

 

[2] Rust의 경우

Rust의 경우 GC를 사용하지 않는다. 모든 체크는 컴파일 타임에 이루어지며, 따라서 런타임 오버헤드가 0에 수렴한다! 이러한 장점 덕분에 Rust는 최근 가장 급격히 떠오르는 프로그래밍 언어가 되었다.

 

1)Ownership

어떤 변수에 오브젝트를 bind하게 되면, 그 변수는 해당 오브젝트에 대해 소유권(ownership)을 갖게 된다. 또한 하나의 오브젝트에 대해서는, 오직 단 하나의 변수만 소유권을 가질 수 있다. 즉, 2개의 변수가 동시에 하나의 오브젝트를 소유할 수 없다. 다음의 예제 코드를 보자.

let x = vec![1,2,3];
let x2 = x;
println!("x[0] : {}", x[0]);	//complie error!

첫 번째 줄에서, x가 벡터 [1,2,3]에 대한 소유권을 갖게 된다. 하지만 두 번째 줄에서, x2가 해당 벡터에 대한 소유권을 물려받게 되고, 동시에 2개의 변수가 동일한 오브젝트를 소유할 수 없으므로, x의 소유권은 소멸된다. 따라서 세 번째 줄에서 소멸된 오브젝트에 대해 접근을 시도하였으므로 컴파일 에러가 발생한다. 

 

Rust는 소유권 개념을 통해 여러 개의 포인터가 하나의 오브젝트를 가리킴으로써 발생하는 data race를 원천적으로 차단할 수 있다.

 

2) Copy trait

let x = 10;
let x2 = x;
x2 = 15;
println!("x : {}, x2 : {}", x, x2);	//10, 15

i32(==int32)와 같이 스택에 저장되는 타입에 소유권 개념을 도입한다면 코딩이 극도로 힘들어지고 효율적이지도 않을 것이다. 따라서 이러한 타입들은 소유권을 넘겨주는 것이 아니라 값을 복제하여 넘겨준다. 따라서 위 코드의 두 번째 줄에서, x2는 x의 소유권을 물려받는 것이 아니라, 새로운 i32를 만들어서 x의 값을 복제하게 된다. 또한 자신이 정의한 오브젝트(구조체와 같은)에 대해서도 copy trait rule을 명시한다면 위와 같이 활용 가능하다.

 

3) Borrow

fn foo(v : Vec<i32>){
	...
}

fn bar(v : &Vec<i32>){
	...
}

let x = vec![1,2,3];
let y = vec![1,2,3];

foo(x);
bar(y);

println!("x[0] : {}", x[0]);	//compile error!
println!("y[0] : {}", y[0]);	//no problem

함수의 인자로 오브젝트를 가리키는 변수를 넘겨줄 때는 어떻게 될까? 1)의 소유권 개념을 적용시켜 보면, 함수 인자로 소유권이 이전되고, 이전 변수의 소유권을 소멸될 것이라고 생각할 수 있다. 따라서 위 프로그램에서 foo(x)로 호출하게 되면 x의 소유권은 소멸되고, 함수의 매개변수 v에서 이를 물려받아 사용한다. 따라서 이후에 x에 접근하는 println 구문에서 컴파일 에러가 발생한다.

 

이와 같은 매개변수로의 소유권 이전은 call-by-reference를 불가능하게 한다. 따라서 이를 허용하기 위해 소유권의 borrow 개념이 등장한다. 위의 bar함수와 같이, 매개변수를 레퍼런스로 선언하게 되면 소유권을 일시적으로 '빌려'오게 된다. 이 소유권은 bar의 scope내에서 유효하며, bar가 리턴되는 순간 원래 소유자인 y에게 되돌아가게 된다. 따라서 위 코드에서 println을 통해 y에 접근하는 것이 가능하다.

 

4) mutability ^ alisasing

3)의 borrow의 개념에서 가장 중요한 한 가지 설명이 남아있다.

1. 변경 가능한(mutable) 레퍼런스를 생성할 수 있지만, 오직 단 한 개의 레퍼런스만이 동시에 존재할 수 있다.
2. 여러 개의 레퍼런스를 가질 수도 있지만(aliasing), 이 레퍼런스들은 변경 불가능(immutable)하다.
3. 1,2를 둘 다 만족하는 레퍼런스를 만들 수는 없다.(XOR)

이해하기 어렵다면, read-write lock을 생각해보자. data race를 막기 위해서는 어떤 한 data에 대해서, (1)단 한 개의 write 가능한 접근. (2) 여러 개의 read만 가능한 접근. 단, (1)과 (2)가 동시에 성립될 수는 없다. Rust의 borrow 개념도 이와 마찬가지로 이해하면 된다.

 

 

참고

https://pcwalton.github.io/2013/05/20/safe-manual-memory-management.html

 

https://pcwalton.github.io/2013/05/20/safe-manual-memory-management.html

If there’s one feature of Rust that is probably the most unique among languages in industry, it’s safe manual memory management. It’s easiest to explain safe manual memory management by explaining how it differs from the memory models of other languages. T

pcwalton.github.io

https://stackoverflow.com/questions/36136201/how-does-rust-guarantee-memory-safety-and-prevent-segfaults

 

How does Rust guarantee memory safety and prevent segfaults?

I was looking for a language to learn, and I saw that Rust is getting quite popular. Two things impressed me about Rust, memory safety and preventing segfaults. How does Rust achieve this? What

stackoverflow.com

https://theburningmonk.com/2015/05/rust-memory-safety-without-gc/

 

Rust – memory safety without garbage collector | theburningmonk.com

I’ve spent time with Rust at various points in the past, and being a language in development it was no surprise that every time I looked there were breaking changes and even the documentations look very different at every turn! Fast forward to May 2015 and

theburningmonk.com

 

'Computer Science > Security' 카테고리의 다른 글

Confidential Computing이란  (0) 2020.03.24
double-free 취약점  (0) 2020.02.04
Memory Safety - 2  (0) 2019.11.12
Memory Safety - 1  (0) 2019.10.30

+ Recent posts