맨위로가기

경쟁 상태

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

경쟁 상태는 여러 프로세스 또는 스레드가 공유 자원에 동시에 접근할 때, 접근 순서나 타이밍에 따라 결과가 달라지는 상황을 의미한다. 이는 전산학, 전자공학, 컴퓨터 보안, 파일 시스템, 네트워킹, 생명 유지 시스템 등 다양한 분야에서 발생할 수 있으며, 특히 소프트웨어 버그, 보안 취약점, 데이터 손상, 시스템 오작동 등 심각한 문제를 야기할 수 있다. 경쟁 상태는 데이터 경합, TOCTTOU 취약점과 같은 형태로 나타나며, 해결을 위해 프로세스 협력 기법, 파일 잠금, 네트워크 상태 관리 등이 사용된다. 또한, 정적/동적 분석 도구 및 벤치마크를 통해 경쟁 상태를 감지하고 예방할 수 있다.

더 읽어볼만한 페이지

  • 분산 컴퓨팅 문제 - 교착 상태
    교착 상태는 둘 이상의 프로세스가 자원을 점유하고 서로의 자원을 요청하여 더 이상 진행할 수 없는 상태를 의미하며, 상호 배제, 점유 대기, 비선점, 순환 대기 네 가지 조건이 모두 충족되어야 발생하고, 운영 체제는 이를 예방, 회피, 무시, 발견하는 방법으로 관리한다.
  • 분산 컴퓨팅 문제 - 비잔티움 장애 허용
    비잔틴 장애 허용(BFT)은 분산 컴퓨팅 시스템에서 일부 구성 요소가 오류나 악의적인 행위를 하더라도 시스템 전체가 정상적으로 작동하도록 보장하는 속성으로, 비잔틴 장애에 대한 대응책으로 합의 알고리즘을 통해 신뢰성을 확보하며, 블록체인, 항공, 군사, 암호화폐 등 다양한 분야에서 활용된다.
  • 안티패턴 - 기술 부채
    기술 부채는 소프트웨어 개발에서 발생하는 개념으로, 현재의 편의적인 설계가 미래에 추가적인 비용을 발생시키는 것을 의미하며, 다양한 원인으로 발생하여 개발 비용 증가, 프로젝트 지연, 경쟁력 약화 등의 부정적인 결과를 초래할 수 있다.
  • 안티패턴 - 난독화
    난독화는 프로그램 코드의 가독성을 낮춰 이해를 어렵게 하는 기술로, 역공학을 방지하여 보안을 강화하며 소스 코드와 바이너리 난독화로 구분되고, 코드 분석을 어렵게 만들지만 불가능하게 하지는 않아 다른 보안 기술과 함께 사용된다.
  • 동시성 제어 - 세마포어
    세마포어는 데이크스트라가 고안한 정수 변수로, P/V 연산을 통해 자원 접근을 제어하고 동기화 문제를 해결하며, 계수 세마포어와 이진 세마포어로 나뉘어 멀티스레드 환경에서 자원 관리 및 스레드 동기화에 기여한다.
  • 동시성 제어 - 모니터 (동기화)
    모니터는 공유 자원 접근을 제어하여 프로세스 간 동기화를 구현하는 프로그래밍 구조로, 뮤텍스 락, 조건 변수 등으로 구성되어 경쟁 상태를 방지하며 여러 프로그래밍 언어에서 지원된다.
경쟁 상태
일반 정보
이름경쟁 상태
다른 이름레이스 컨디션, 레이스 해저드
정의시스템의 동작이 통제 불가능한 사건들의 타이밍에 의존하는 상황
예시
소프트웨어동시에 실행되는 여러 스레드가 공유 자원에 접근할 때 발생할 수 있음
공유 변수 업데이트 시 순서에 따라 결과가 달라질 수 있음
하드웨어여러 신호가 동시에 도착할 때 최종 상태가 신호 도착 순서에 따라 달라질 수 있음
플립플롭 회로에서 발생할 수 있음
해결 방법
소프트웨어뮤텍스
세마포어
임계 구역
원자적 연산
하드웨어플립플롭 회로 설계 개선
메타스테빌리티 방지 회로 추가
관련 개념
교착 상태여러 프로세스가 서로의 자원 획득을 기다리며 진행이 멈추는 상태
상호 배제공유 자원에 대한 동시 접근을 막는 메커니즘
임계 구역공유 자원에 접근하는 코드 영역으로, 상호 배제가 필요함
메타스테빌리티플립플롭 회로의 출력값이 불안정하게 진동하는 상태
참고 자료
문서스레드 동기화에 대한 소개
참고WikiWikiWeb RaceCondition 항목
Critical Section

2. 전산학에서의 경쟁 상태

전산학에서 '''경쟁 상태'''란 공유 자원에 대해 여러 프로세스가 동시에 접근할 때, 접근 타이밍이나 순서에 따라 결과값이 달라지는 현상을 말한다. 이는 자료의 일관성을 해칠 수 있다.[3][4]

경쟁 상태는 주로 여러 프로세스나 스레드가 통신하며 동작하는 병렬 컴퓨팅 환경에서 발생한다. 하지만 단일 스레드 환경에서도 이벤트 구동형 프로그래밍처럼 비동기 처리를 하거나, 시그널에 의한 인터럽트 발생 시에도 나타날 수 있다.

소프트웨어에서 경쟁 상태는 동시에 실행되는 여러 코드 경로가 예상과 다른 실행 시간을 가질 때 발생하여, 예상치 못한 동작이나 소프트웨어 버그를 유발할 수 있다. 또한, 프로그램 간 경쟁은 보안 문제로 이어지기도 한다.

치명적인 경쟁 상태는 잘못된 실행과 소프트웨어 버그를 일으킨다. 이는 주로 프로세스나 스레드가 공유 상태에 의존하고, 상호 배제가 보장되어야 하는 임계 구역에서 공유 상태에 대한 작업이 제대로 처리되지 않을 때 발생한다.

데이터 경쟁은 경쟁 상태의 한 유형으로, 다양한 메모리 모델에서 중요하게 다뤄진다. C11 및 C++11 표준에 정의된 메모리 모델에 따르면, 데이터 경쟁을 포함하는 C 또는 C++ 프로그램은 정의되지 않은 동작을 갖는다.

경쟁 상태는 최종 결과가 비결정적 알고리즘이며, 스레드 간 상대적 타이밍에 의존하기 때문에 재현 및 디버깅이 어렵다. 디버깅 중에는 문제가 사라지는 것처럼 보일 수 있는데, 이러한 버그를 "하이젠버그"라고 부르기도 한다. 따라서 신중한 소프트웨어 설계를 통해 경쟁 상태를 예방하는 것이 중요하다.

정보 처리 관점에서 경쟁 상태는 "이벤트 타이밍에 대한 예기치 않은 의존성이 초래하는 비정상적인 동작"으로 정의된다.[28]
경쟁 상태 발생 예시


  • "read-modify-write" (상태 읽기-변경-쓰기) 연산:


병렬로 동작하는 두 스레드 T1, T2가 전역 변수 `i` (초깃값 0)를 각각 1씩 증가시키는 상황을 가정해 보자. 이상적인 처리는 다음과 같다.

1. T1이 i 값을 읽음 (0)

2. T1이 i 값을 1 증가 (1)

3. T2가 i 값을 읽음 (1)

4. T2가 i 값을 1 증가 (2)

하지만 락이나 동기화 없이는 다음과 같은 잘못된 결과가 발생할 수 있다.

1. T1이 i 값을 읽음 (0)

2. T2가 i 값을 읽음 (0)

3. T1이 i 값을 1 증가 (1)

4. T2가 i 값을 1 증가 (1)

최종적으로 i 값은 2가 아닌 1이 된다.

  • "check-then-act" (조건 확인 후 동작) 연산[29] (Time of check to time of use[30]):


다음 의사 코드를 보자.

```

global integer A = 0;

// A를 증가시키고 "RX" 출력 (인터럽트 발생 시 실행)

task Received()

{

A = A + 1;

print "RX";

}

// A가 짝수일 때만 A 출력 (1초마다 실행)

task Timeout()

{

if (A is divisible by 2)

{

print A;

}

}

```

다음과 같은 출력 결과를 얻을 수 있다.

```

0

0

0

RX

RX

2

RX

RX

4

4

```

다음 순서로 이벤트가 발생하면 문제가 발생한다.

1. `Timeout` 태스크 실행, A가 짝수임을 확인

2. 인터럽트 발생, `Received` 태스크 실행

3. `Received` 태스크가 A를 증가시키고 "RX" 출력

4. `Timeout` 태스크로 복귀

5. A를 출력하지만, A의 현재 값(홀수)을 출력

뮤텍스는 이러한 병렬 프로그래밍 문제를 해결하는 데 사용된다.

'''데이터 경합'''()은 경쟁 상태와 유사하지만, 단일 데이터에 대한 동시 읽기/쓰기로 인해 비일관성이 발생하는 현상을 의미한다. 데이터 경합은 상호 배제를 통해 방지할 수 있지만, 경쟁 상태는 더 큰 단위의 동기화 메커니즘 (예: 뮤텍스)을 필요로 할 수 있다.

2. 1. 해결 방법

전산학에서 '''경쟁 상태'''를 방지하기 위해서는 프로세스 협력 기법이 필요하다. 경쟁 상태를 해결하기 위한 방법은 다음과 같다.

3. 전자공학에서의 경쟁 상태

전자공학에서 경쟁 상태는 논리 게이트의 입력 신호가 동일한 소스에서 출발하여 서로 다른 경로를 거쳐 도착할 때 발생한다. 이때, 입력 신호의 변화 시간 차이로 인해 출력은 의도하지 않은 상태로 잠시 변경될 수 있다.[2] 예를 들어, 출력이 메모리를 포함하는 다른 시스템의 클럭 신호로 사용되는 경우 시스템이 설계된 동작에서 벗어날 수 있다.

예를 들어, 두 개의 입력을 가진 AND 게이트에서 한 입력에는 논리 신호 A가, 다른 입력에는 A의 부울 부정인 \neg A가 들어오는 경우를 생각해 보자. 이론적으로는 A \wedge \overline{A} \ne 1 이므로 출력은 참 값을 가질 수 없다. 그러나 A의 값이 거짓에서 참으로 바뀔 때, 두 번째 입력으로 값이 전달되는 시간이 첫 번째 입력보다 길다면, 짧은 시간 동안 두 입력이 모두 참이 되어 출력도 참이 될 수 있다.[2]

논리 회로가 카운터의 특정 출력을 감지하는 데 사용될 때도 경쟁 상태가 발생할 수 있다. 카운터의 모든 비트가 정확히 동시에 변경되지 않으면 중간 패턴이 생겨 잘못된 결과를 낼 수 있다.

이러한 경쟁 상태는 카르노 맵과 같은 설계 기법을 통해 해결할 수 있다.

3. 1. 임계 및 비임계 경쟁 상태

임계 경쟁 상태는 내부 변수가 변경되는 순서에 따라 최종 상태 머신의 상태가 결정되는 경우를 말한다.

비임계 경쟁 상태는 내부 변수가 변경되는 순서와 무관하게 최종 상태 머신의 상태가 결정되는 경우를 말한다.

3. 2. 정적, 동적 및 본질적 경쟁 상태

논리 게이트가 동일한 소스에서 서로 다른 경로를 따라 이동한 신호를 결합할 때 경쟁 상태가 발생할 수 있다. 게이트의 입력은 소스 신호의 변화에 따라 약간 다른 시간에 변경될 수 있다. 출력은 의도된 상태로 다시 돌아가기 전에 잠시 동안 원치 않는 상태로 변경될 수 있다.[2]

  • '''정적 경쟁 상태''': 신호와 그 신호의 보수(NOT)가 결합될 때 발생한다.
  • '''동적 경쟁 상태''': 단 하나의 전환만 의도되었는데 여러 개의 전환이 발생할 때 발생한다. 이는 게이트 간의 상호 작용으로 인해 발생하며, 2단계 이하의 게이팅을 사용하면 제거할 수 있다.
  • '''본질적 경쟁 상태''': 입력이 총 피드백 전파 시간보다 짧은 시간에 두 번 전환될 때 발생한다. 때로는 유도 지연선 요소를 사용하여 입력 신호의 시간 지속 시간을 효과적으로 늘려 해결한다.

3. 3. 해결 방법

카르노 맵과 같은 설계 기법을 통해 설계자는 문제 발생 전에 경쟁 상태를 인식하고 제거할 수 있다. 종종 논리 중복성을 추가하여 일부 종류의 경쟁을 제거할 수 있다.[1]

이러한 문제 외에도 일부 논리 요소는 준안정 상태에 진입하여 회로 설계자에게 추가적인 문제를 발생시킬 수 있다.[1]

경합 상태의 전형적인 예는 논리 회로 시스템에서 입력이 변화할 때 발생하는 것이다. 어떤 출력이 입력의 상태에 의존하는 경우, 이는 정상 상태 신호에 관해서만 정의될 수 있다. 입력의 상태가 변화할 때, 전자 시스템의 물리적 특성으로 인해 출력이 변화하기까지 어느 정도의 지연이 발생한다. 그동안, 출력은 정의된 상태 이외의 불안정한 상태가 될 수 있다. 이러한 일시적인 장애를 허용하는 시스템도 있지만, 예를 들어 그 출력 신호가 다른 메모리 등을 포함하는 시스템의 클럭으로 사용되는 경우, 시스템은 설계된 것과는 다른 동작을 할 수 있다.[1]

예를 들어, 2입력 AND 게이트에서 논리 신호 X와 그 부정 NOT X를 입력으로 사용하는 경우를 생각해 보자. 이론상, 그 출력(X AND NOT X)은 ON이 될 수 없다. 그러나 신호 X가 그대로 입력되는 측과 NOT 게이트를 통해 입력되는 측에서 지연 시간에 차이가 있는 경우, 짧은 시간 동안이지만, 일시적으로 AND 게이트의 출력이 ON이 되는 경우가 있다.[1]

4. 소프트웨어에서의 경쟁 상태

소프트웨어에서 경쟁 상태는 컴퓨터 프로그램이 동시에 실행되는 여러 코드 경로를 가질 때 발생할 수 있다. 여러 코드 경로가 예상과 다른 시간이 걸리면 예상과 다른 순서로 완료될 수 있으며, 이는 예상치 못한 동작으로 인해 소프트웨어 버그를 유발할 수 있다.[28] 또한 두 프로그램 간에 경쟁이 발생하여 보안 문제가 발생할 수도 있다.

치명적인 경쟁 상태는 잘못된 실행과 소프트웨어 버그를 유발한다. 치명적인 경쟁 상태는 종종 프로세스 또는 스레드가 일부 공유 상태에 의존할 때 발생한다. 공유 상태에 대한 작업은 임계 구역에서 수행되며, 이는 상호 배제되어야 한다. 이 규칙을 따르지 않으면 공유 상태가 손상될 수 있다.

경쟁 상태는 최종 결과가 비결정적 알고리즘이며 간섭하는 스레드 간의 상대적 타이밍에 따라 달라지기 때문에 재현하고 디버그하기 어려울 수 있다. 따라서 신중한 소프트웨어 설계를 통해 경쟁 상태를 피하는 것이 좋다. 디버깅 시도 중에 사라지는 이러한 유형의 버그를 "하이젠버그"라고 부른다.

정보 처리에서 경쟁 상태는 "이벤트 타이밍에 대한 예기치 않은 의존성이 초래하는 비정상적인 동작"이다.[28] 특히 여러 프로세스나 스레드가 통신하면서 동작하는 경우 (병렬 컴퓨팅)에 발생하지만, 단일 스레드로 동작하는 경우라도 이벤트 구동형 프로그래밍과 같은 비동기 처리로 여러 처리의 실행 순서가 바뀌거나, 시그널에 의한 인터럽트가 원인이 되어 발생하기도 한다.

"read-modify-write" ( "상태 읽기, 변경, 상태 쓰기"를 의미하는 전형적인 처리)에서 발생하는 경쟁 상태의 예로, 병렬로 동작하는 두 개의 스레드 T1과 T2가 각각 정수형의 공유 변수 (전역 변수) `i`를 1씩 증가시키는 경우가 있다. 이상적으로는 T1이 `i` 값을 읽고 증가시킨 후 T2가 `i` 값을 읽고 증가시켜 최종적으로 `i` 값이 2가 되어야 한다. 그러나 두 스레드가 병렬로 동작하며 락이나 동기화 기구를 사용하지 않으면, T1과 T2가 거의 동시에 `i` 값을 읽고 각자 증가시켜 최종적으로 `i` 값이 1이 되는 잘못된 결과가 발생할 수 있다.

"check-then-act" ( "조건 확인 (check), 그리고 (then) 조건에 따른 동작 (act)"을 의미하는 전형적인 처리)에서 발생하는 경쟁 상태의 예시는 데이터 경합 부분에서 설명과 함께 제시되어 있다.

뮤텍스는 병렬 프로그래밍에서 이러한 문제에 대처하기 위해 사용된다.

경쟁 상태와 비슷한 개념으로 '''데이터 경합'''()이 있다. 데이터 경합과 경쟁 상태의 차이에 대해서는 데이터 경합 부분에서 설명되어 있다.

4. 1. 데이터 경합 (Data Race)

전산학에서 '''데이터 경합'''(영어: data race)은 경쟁 상태의 한 종류이다. 여러 스레드가 동시에 같은 메모리 위치에 접근하여 읽고 쓸 때 발생하며, 이로 인해 데이터의 일관성을 해치는 결과가 나타날 수 있다.

데이터 경합에 대한 정확한 정의는 사용되는 공식적인 동시성 모델에 따라 다르지만, 일반적으로 한 스레드의 메모리 연산이 다른 스레드의 메모리 연산과 동시에 메모리 위치에 접근하려고 시도할 때 발생한다. 특히, 한 스레드가 쓰기를 시도하는 동안 다른 스레드가 읽거나 쓰는 상황이 위험하다.

이는 두 스레드가 동시에 메모리 위치에 쓰기를 시도하는 경우, 해당 메모리 위치가 각 스레드가 쓰려고 시도한 값을 나타내는 비트의 임의적이고 의미 없는 조합을 가질 수 있기 때문에 위험하다. 이는 결과 값이 두 스레드 모두 쓰려고 시도하지 않은 값일 경우 메모리 손상으로 이어질 수 있다 (때로는 이를 '찢어진 쓰기'라고 한다). 마찬가지로, 한 스레드가 다른 스레드가 쓰고 있는 동안 위치에서 읽는 경우, 읽기가 쓰기 전 메모리 위치가 가지고 있던 값을 나타내는 비트와 쓰여지고 있는 값을 나타내는 비트의 임의적이고 의미 없는 조합을 반환할 수 있다.

C11 및 C++11 표준에 정의된 메모리 모델은 데이터 경합을 포함하는 C 또는 C++ 프로그램이 정의되지 않은 동작을 갖는다고 명시한다.[3][4]

''C++ 표준'' 초안 N4296 (2014-11-19)에서는 데이터 레이스를 다음과 같이 정의한다.[6]

> 두 개의 액션이 다음 경우에 ''잠재적으로 동시적''이다.

> * 서로 다른 스레드에 의해 수행되거나,

> * 순서가 정해지지 않았고, 적어도 하나는 시그널 핸들러에 의해 수행된다.

>

> 프로그램의 실행은 잠재적으로 동시적인 두 개의 충돌하는 액션을 포함하고, 적어도 하나는 원자적이지 않으며, 어느 것도 다른 것보다 먼저 발생하지 않는 경우, 데이터 레이스를 포함한다. 단, 아래에 설명된 시그널 핸들러의 특별한 경우는 예외이다 [생략]. 이러한 데이터 레이스는 정의되지 않은 동작을 초래한다.

Java 언어 사양[8]은 C++와는 다른 정의를 제공하는데, C++에서 데이터 레이스는 정의되지 않은 동작인 반면, Java에서 데이터 레이스는 단순히 "스레드 간 액션"에 영향을 미친다.[8]

데이터 경합은 뮤텍스와 같은 상호 배제 기법을 사용하여 방지할 수 있다. 하지만 데이터 경합과 경쟁 상태는 다른 개념이므로, 데이터 경합을 회피하더라도 경쟁 상태를 완전히 회피할 수는 없다. 원자적 연산이어야 하는 트랜잭션과 같이, 동작의 타이밍에 관계없이 처리 결과의 일관성을 유지해야 하는 경우에는, 더 큰 단위의 상호 배제가 가능한 뮤텍스 등의 동기화·조정 기구를 사용할 필요가 있다.

데이터 경합은 경쟁 상태와 비슷하지만, 모든 사람들이 데이터 경합을 경쟁 상태의 하위 집합으로 간주하는 것은 아니다.[5] 데이터 경합이 없는 프로그램에서도 타이밍으로 인해 비결정성이 발생할 수 있다. 예를 들어, 모든 메모리 접근이 원자적 연산만 사용하는 프로그램에서 그렇다.

4. 2. 예시

두 개의 스레드가 전역 정수 변수를 동시에 1씩 증가시키는 상황을 가정해 보자. 이상적인 경우, 각 스레드는 값을 읽고, 1을 더하고, 다시 쓰는 작업을 순차적으로 수행하여 최종적으로 값이 2가 되어야 한다.

스레드 1스레드 2정수 값
0
값 읽기0
값 증가0
다시 쓰기1
값 읽기1
값 증가1
다시 쓰기2



그러나 세마포어 같은 동기화 장치 없이 두 스레드가 동시에 실행되면, 연산 순서가 꼬여 잘못된 결과가 나올 수 있다. 다음은 그러한 예시이다.

스레드 1스레드 2정수 값
0
값 읽기0
값 읽기0
값 증가0
값 증가0
다시 쓰기1
다시 쓰기1



이 경우, 최종 값은 2가 아닌 1이 된다. 이는 증가 연산이 상호 배제되지 않아 중단될 수 있기 때문이다.

"read-modify-write"(읽기-수정-쓰기) 패턴에서 발생하는 경쟁 상태의 예시는 다음과 같다. 두 스레드 T1과 T2가 공유 변수 `i`를 1씩 증가시킬 때, 동기화 없이는 다음과 같은 문제가 발생할 수 있다.

# i = 0;

# T1이 i 값을 읽어 레지스터에 저장한다: 0

# T2가 i 값을 읽어 레지스터에 저장한다: 0

# T1이 i 값을 증가시킨다: (i의 현재 값) + 1 = 1

# T2가 i 값을 증가시킨다: (i의 현재 값) + 1 = 1

결과적으로 i 값은 2가 아닌 1이 된다.

"check-then-act"(확인-후-실행) 패턴에서도 경쟁 상태가 발생할 수 있다. 다음은 두 태스크의 의사 코드 예시이다.

```

global integer A = 0;

// A의 값을 증가시키고 "RX"를 표시한다

// 터미널로부터의 인터럽트가 발생할 때마다 기동된다

task Received()

{

A = A + 1;

print "RX";

}

// A가 짝수일 때만 그것을 표시한다

// 1초 간격으로 기동된다

task Timeout()

{

if (A is divisible by 2)

{

print A;

}

}

```

다음과 같은 순서로 이벤트가 발생하면 문제가 된다.

# Timeout 태스크가 A를 확인하고 짝수여서 출력하려 한다.

# 인터럽트 발생으로 Received 태스크가 실행되어 A를 증가시키고 "RX"를 출력한다.

# Timeout 태스크로 돌아와 A를 출력하지만, A의 현재 값은 홀수이므로 다른 값이 출력된다.

이러한 문제를 해결하기 위해 뮤텍스와 같은 병렬 프로그래밍 기법이 사용된다.

경쟁 상태와 비슷한 개념으로 '''데이터 경합'''(data race영어)이 있다. 데이터 경합은 단일 데이터에 대한 동시 읽기/쓰기로 인해 비일관성이 발생하는 현상이다. 데이터 경합은 상호 배제를 통해 회피할 수 있지만, 경쟁 상태는 더 큰 단위의 상호 배제가 필요할 수 있다.

4. 3. 디버깅의 어려움

경쟁 상태는 최종 결과가 비결정적 알고리즘이며 간섭하는 스레드 간의 상대적 타이밍에 따라 달라지기 때문에 재현하고 디버그하기 어려울 수 있다. 이러한 성격의 문제는 디버그 모드로 실행하거나, 추가 로깅을 추가하거나, 디버거를 연결할 때 사라질 수 있다. 이처럼 디버깅 시도 중에 사라지는 버그는 종종 "하이젠버그"라고 불린다.[28] 따라서 신중한 소프트웨어 설계를 통해 경쟁 상태를 피하는 것이 좋다.

5. 컴퓨터 보안에서의 경쟁 상태

경쟁 상태는 공유 자원에 접근하는 공격자가 다른 사용자의 오작동을 유발하여 서비스 거부[13] 공격이나 권한 상승[16][14]을 일으킬 수 있게 한다.

5. 1. TOCTTOU 취약점

소프트웨어의 많은 경쟁 상태는 컴퓨터 보안과 관련된 문제점을 가지고 있다. 경쟁 상태는 공유 리소스에 접근할 수 있는 공격자가 해당 리소스를 사용하는 다른 행위자가 오작동을 일으키도록 하여 서비스 거부[13] 및 권한 상승[16][14]을 포함한 결과를 초래할 수 있게 한다.

특정 종류의 경쟁 상태는 인증과 같이 참/거짓 여부를 판단하는 과정(술어)을 확인한 다음, 상태가 "확인 시점"과 "사용 시점" 사이에 변경될 수 있는 경우, 해당 술어에 따라 행동하는 것을 포함한다. 이러한 종류의 버그가 보안에 민감한 코드에 존재할 때, 시간 검사 사용 취약점(Time-of-Check to Time-of-Use, TOCTTOU) 버그라고 하는 보안 취약점이 생성된다.

예를 들어, 파일의 접근 권한을 확인한 후에 실제 파일 열기를 하는 경우(이는 운영 체제 내에서 일어나는 일이다), 확인과 열기 사이에 파일을 바꾸는(예: 심볼릭 링크로 만드는) 경우, 일반적으로 접근할 수 없는 파일에 접근할 수 있다. (이는 운영 체제에 버그가 있는 경우에 한정되며, 일반적으로 가능하다는 의미는 아니다.)

5. 2. 하드웨어 보안

경쟁 상태는 소프트웨어뿐만 아니라 하드웨어 보안과도 관련이 깊다. 공격자는 경쟁 상태를 악용하여 공유 자원에 접근, 시스템을 오작동시켜 서비스 거부[13] 공격이나 권한 상승[16][14]을 일으킬 수 있다.

반면, 경쟁 상태는 하드웨어 난수 생성기 및 물리적 복제 방지 기능(PUF)을 만드는 데 의도적으로 사용되기도 한다.[15] PUF는 동일한 경로를 갖는 회로 토폴로지를 설계하고 제조 과정의 미세한 변동에 따라 어떤 경로가 먼저 완료될지 무작위로 결정되도록 하여 생성된다. 각 회로의 경쟁 상태 결과를 측정, 프로파일을 만들어 비밀로 유지함으로써 추후 회로의 신원을 확인하는 데 사용한다.

6. 파일 시스템에서의 경쟁 상태

둘 이상의 프로그램이 파일 시스템을 수정하거나 접근하려는 시도에서 충돌이 발생할 수 있으며, 이로 인해 데이터 손상이나 권한 상승이 발생할 수 있다.[16]

6. 1. 해결 방법

파일 잠금은 경쟁 상태를 해결하는 일반적인 방법이다.[16] 더 복잡하지만, 한 가지 해결책은 하나의 고유한 프로세스(데몬 또는 유사한 프로그램 실행)가 파일에 대한 독점적인 접근 권한을 갖도록 시스템을 구성하고, 해당 파일의 데이터에 접근해야 하는 다른 모든 프로세스가 프로세스 간 통신을 통해서만 접근하도록 하는 것이다. 이는 프로세스 수준에서 동기화가 필요하다.

파일 시스템에서 파일 잠금은 경쟁 상태의 일반적인 해결책이다. 다른 해결책으로는, 어떤 파일에 대해 하나의 프로세스가 배타적인 접근 권한을 가지고 (데몬과 같은 동작을 수행), 다른 프로세스가 해당 파일에 접근하고자 할 때 프로세스 간 통신으로 해당 프로세스에 요청하는 방식이 있다(물론, 이 과정에서 프로세스 레벨의 동기화가 필요하다).

6. 2. 기타 경쟁 상태

둘 이상의 프로그램이 파일 시스템을 수정하거나 접근하려는 시도에서 충돌이 발생할 수 있으며, 이로 인해 데이터 손상이나 권한 상승이 발생할 수 있다.[16] 파일 잠금은 일반적으로 사용되는 해결책이다. 더 번거로운 해결책은 하나의 고유한 프로세스(데몬 또는 유사한 프로그램 실행)가 파일에 대한 독점적인 접근 권한을 갖도록 시스템을 구성하고, 해당 파일의 데이터에 접근해야 하는 다른 모든 프로세스가 해당 프로세스와의 프로세스 간 통신을 통해서만 접근하도록 하는 것이다. 이는 프로세스 수준에서 동기화가 필요하다.

관련 없는 프로그램들이 디스크 공간, 메모리 공간 또는 프로세서 사이클과 같은 사용 가능한 리소스를 갑자기 소모하여 서로 영향을 미치는 파일 시스템에도 다른 형태의 경쟁 상태가 존재한다. 이러한 경쟁 상황을 예상하고 처리하도록 신중하게 설계되지 않은 소프트웨어는 예측할 수 없게 될 수 있다. 이러한 위험은 매우 안정적으로 보이는 시스템에서 오랫동안 간과될 수 있다. 그러나 결국 충분한 데이터가 축적되거나 충분한 다른 소프트웨어가 추가되어 시스템의 많은 부분을 치명적으로 불안정하게 만들 수 있다. 이러한 예는 화성 탐사 로버 "스피릿"이 착륙한 지 얼마 되지 않아 발생한 사건으로, 삭제된 파일 항목으로 인해 파일 시스템 라이브러리가 사용 가능한 모든 메모리 공간을 소모하면서 발생했다.[17] 해결책은 소프트웨어가 작업을 시작하기 전에 필요한 모든 리소스를 요청하고 예약하는 것이다. 이 요청이 실패하면 작업이 연기되어 실패가 발생할 수 있는 많은 지점을 피할 수 있다. 또는 이러한 각 지점에 오류 처리를 갖추거나 전체 작업의 성공 여부를 나중에 확인한 다음 계속할 수 있다. 더 일반적인 접근 방식은 작업을 시작하기 전에 충분한 시스템 리소스가 사용 가능한지 확인하는 것이다. 그러나 복잡한 시스템에서는 다른 실행 중인 프로그램의 동작을 예측할 수 없기 때문에 이것만으로는 충분하지 않을 수 있다.

7. 네트워킹에서의 경쟁 상태

분산 시스템 환경에서는 공유 자원에 대한 동시 접근이 문제가 될 수 있다. 예를 들어, IRC와 같은 분산 채팅 네트워크에서, 서로 다른 서버에 접속한 두 사용자가 동시에 같은 이름의 채널을 개설하려고 할 때, 각 서버는 상대 서버의 신호를 받기 전에 사용자에게 운영자 권한을 부여할 수 있다. 이는 네트워크 지연 시간으로 인해 발생하며, 현재는 많은 IRC 서버 구현에서 대부분 해결되었다.

7. 1. 예시: IRC 채널 운영자 권한 문제

IRC를 사용하는 네트워크 환경에서, 서로 다른 서버에 있는 두 명의 사용자가 동시에 같은 이름의 채널을 개설하려고 시도하는 상황을 생각해 보자. 각 사용자의 서버는 상대 서버로부터 채널 생성 신호를 받기 전에 각 사용자에게 채널 운영자 권한을 부여하게 된다. 이는 네트워크 지연 시간 때문에 발생하는 현상이다. 이 문제는 다양한 IRC 서버 구현을 통해 대부분 해결되었다.

이러한 경쟁 상태에서 "공유 자원"은 네트워크 상태(어떤 채널이 존재하고, 누가 채널을 시작했으며, 어떤 권한을 갖는지 등)를 포함한다. 각 서버는 네트워크 상태를 자유롭게 변경할 수 있지만, 변경 사항을 다른 서버에 알리기까지 지연 시간이 발생하여 경쟁 상태가 발생할 수 있다. 이 문제를 해결하기 위해 공유 자원에 대한 접근을 제어하는 중앙 집중식 시스템을 도입하면(예: 한 서버가 권한을 제어), 분산 네트워크가 중앙 집중식 네트워크로 바뀌게 된다.

7. 2. 해결 방법

네트워킹에서 분산 채팅 네트워크인 IRC를 생각해 볼 수 있다. IRC에서 채널을 시작하는 사용자는 자동적으로 채널 운영자 권한을 얻는다. 서로 다른 네트워크의 서로 다른 서버에 있는 두 명의 사용자가 동시에 동일한 이름의 채널을 시작하려고 하면, 각 사용자의 해당 서버는 각 사용자에게 채널 운영자 권한을 부여한다. 이는 어떤 서버도 해당 채널을 할당했다는 다른 서버의 신호를 아직 받지 못했기 때문이다. (이 문제는 다양한 IRC 서버 구현으로 대부분 해결되었다.)

이러한 경쟁 상태의 경우, "공유 자원"의 개념은 네트워크의 상태(어떤 채널이 존재하는지, 어떤 사용자가 채널을 시작했는지, 따라서 어떤 권한을 가지고 있는지)를 포함하며, 각 서버는 네트워크의 다른 서버에 변경 사항을 알려 네트워크 상태에 대한 인식을 업데이트할 수 있는 한 자유롭게 변경할 수 있다. 그러나 네트워크 전체의 지연 시간으로 인해 이러한 종류의 경쟁 상태가 발생할 수 있다. 이 경우, 공유 자원에 대한 접근을 제어하는 형태를 부과하여 경쟁 상태를 방지하는 것—예를 들어, 한 서버를 지정하여 누가 어떤 권한을 가지고 있는지 제어—은 분산 네트워크를 (적어도 네트워크 운영의 해당 부분에 대해서는) 중앙 집중식 네트워크로 바꾸는 것을 의미한다.

경쟁 상태는 컴퓨터 프로그램이 논블로킹 소켓으로 작성된 경우에도 발생할 수 있으며, 이 경우 프로그램의 성능은 네트워크 링크의 속도에 따라 달라질 수 있다.

사용자가 그러한 중앙 집중식 해결책을 받아들일 수 없는 경우, 경쟁 상태를 감지하고 나중에 그것을 수정하는 등의 처리가 필요하게 된다.[1]

8. 생명 유지 시스템에서의 경쟁 상태

생명 유지 시스템에서 소프트웨어 결함은 치명적인 결과를 초래할 수 있다. 특히 경쟁 상태는 세락 25 방사선 치료 장치 사고[18]와 2003년 북미 정전 사태[19]와 같이 심각한 문제를 일으킬 수 있다.

8. 1. 사례: Therac-25 방사선 치료 기기 사고

생명 유지 시스템의 소프트웨어 결함은 치명적일 수 있다. 경쟁 상태는 최소 3명의 환자를 사망시키고 여러 명에게 부상을 입힌 세락 25 방사선 치료 장치의 결함 중 하나였다.[18]

경합 상태가 치명적인 문제를 일으킨 사례로 방사선 치료 장비 세락 25 사고가 있다. 이 장치의 제어를 담당하는 실시간 운영 체제에는 경합 상태로 인해 발생하는 버그가 존재했는데, 이 판별하기 어려운 버그 때문에, 조작 명령을 빠르게 입력하면 세락 25에서는 X선용 금속 타겟을 제대로 배치하지 않은 채 고에너지 방사선을 조사하는 설정이 가능해졌다.[31][32] 애초에 이 장치는 기존 기기에 탑재되어 있던 전기 기계식 안전 보호 장치 (하드웨어 인터록)를 제거하고 소프트웨어 제어로 대체해 버렸다. 결과적으로 현저하게 드러난 소프트웨어 버그가 문제를 일으켜 6건의 중대한 피폭 사고를 일으키고, 5명의 환자가 사망하게 되었다.[33]

8. 2. 사례: 2003년 북미 정전 사태

오하이오주에 본사를 둔 퍼스트에너지(FirstEnergy Corp)의 에너지 관리 시스템에서 발생한 사례이다. 이 시스템은 GE 에너지(GE Energy)가 제공하였으며, 여러 발전 시설에서 사용되었다.[19] 경보 하위 시스템에 경쟁 상태가 존재했는데, 세 개의 처진 전선이 동시에 작동될 때 모니터링 기술자에게 경고를 발생시키지 못하게 하여 문제 인식을 지연시켰다. 이 소프트웨어 결함은 결국 2003년 북미 정전 사태로 이어졌다.[19] GE 에너지는 나중에 이전에 발견되지 않았던 오류를 수정하는 소프트웨어 패치를 개발했다.

9. 경쟁 상태 감지 도구

소프트웨어에서 경쟁 상태를 감지하는 데 도움이 되는 많은 소프트웨어 도구가 존재한다. 이들은 크게 정적 분석 도구와 동적 분석 도구, 두 그룹으로 분류할 수 있다.

데이터 경쟁 감지 도구의 효과를 평가하는 벤치마크로는 DataRaceBench[24]가 있다. 이는 OpenMP로 작성된 다중 스레드 응용 프로그램을 분석하는 데이터 경쟁 감지 도구를 체계적이고 정량적으로 평가하기 위해 설계되었다.

9. 1. 정적 분석 도구

Thread Safety Analysis는 주석 기반의 절내 정적 분석을 위한 정적 분석 도구로, 원래 gcc의 분기로 구현되었으며 현재 Clang에서 재구현되어 PThreads를 지원한다.[20]

9. 2. 동적 분석 도구

동적 분석 도구는 다음과 같다.

  • Intel Inspector: C/C++ 및 Fortran 응용 프로그램의 신뢰성, 보안 및 정확성을 높이기 위한 메모리 및 스레드 검사 및 디버깅 도구이다.
  • Intel Advisor: C, C++, C#, Fortran 소프트웨어 개발자 및 설계자를 위한 샘플링 기반의 SIMD 벡터화 최적화 및 공유 메모리 스레딩 지원 도구이다.
  • ThreadSanitizer: 바이너리(Valgrind 기반) 또는 소스, LLVM 기반 계측을 사용하며 PThreads를 지원한다.[21]
  • Helgrind: POSIX pthreads 스레딩 기본 요소를 사용하는 C, C++ 및 Fortran 프로그램에서 동기화 오류를 감지하기 위한 Valgrind 도구이다.[22]
  • Data Race Detector: Go 프로그래밍 언어에서 데이터 경쟁을 찾도록 설계되었다.[23]

9. 3. 벤치마크

DataRaceBench[24]OpenMP로 작성된 다중 스레드 응용 프로그램을 분석하는 데이터 경쟁 감지 도구를 체계적이고 정량적으로 평가하기 위해 설계된 벤치마크 모음이다.

10. 기타 분야에서의 경쟁 상태

신경과학에서는 포유류의 뇌에서도 경쟁 상태가 발생할 수 있음을 보여주고 있다.[25][26]

영국 철도 신호에서 경쟁 상태는 규칙 55를 수행할 때 발생할 수 있었다. 이 규칙에 따르면, 열차가 신호에 의해 운행선에서 정지하면 기관차 화부(fireman)는 신호수에게 열차가 있음을 알리기 위해 신호실로 걸어가야 했다. 1934년 윈윅에서 발생한 사고는 신호수가 화부가 도착하기 전에 다른 열차를 허용했기 때문에 발생했다. 현대 신호 방식은 운전자가 라디오로 신호실에 즉시 연락할 수 있게 함으로써 이러한 경쟁 상태를 제거했다.

참조

[1] 논문 The synthesis of sequential switching circuits. https://dspace.mit.e[...] 1954
[2] 학술지 Hazards, Critical Races, and Metastability https://academiccomm[...] 1995-06
[3] 웹사이트 ISO/IEC 9899:2011 - Information technology - Programming languages - C http://www.iso.org/i[...] Iso.org 2018-01-30
[4] 웹사이트 ISO/IEC 14882:2011 http://www.iso.org/i[...] ISO 2011-09-02
[5] 웹사이트 Race Condition vs. Data Race https://blog.regehr.[...] 2011-03-13
[6] 웹사이트 Working Draft, Standard for Programming Language C++ http://www.open-std.[...] 2014-11-19
[7] 논문 Detecting Data Races on Weak Memory Systems https://www.research[...] 1991
[8] 웹사이트 Chapter 17. Threads and Locks https://docs.oracle.[...]
[9] 웹사이트 Semantics of Shared Variables & Synchronization (a.k.a. Memory Models) https://www.hboehm.i[...]
[10] 학위논문 Designing Memory Consistency Models For Shared-Memory Multiprocessors http://sadve.cs.illi[...] 2021-12-09
[11] 논문 Programming for Different Memory Consistency Models http://pages.cs.wisc[...] 1992
[12] 학위논문 Efficient Coherence and Consistency for Specialized Memory Hierarchies University of Illinois at Urbana–Champaign 2017
[13] 웹사이트 CVE-2015-8461: A race condition when handling socket errors can lead to an assertion failure in resolver.c https://kb.isc.org/a[...] Internet Systems Consortium 2017-06-05
[14] 웹사이트 security: stat cache *very large* race condition if caching when follow_symlink disabled https://redmine.ligh[...] lighttpd 2017-06-05
[15] 서적 2008 10th International Symposium on Symbolic and Numeric Algorithms for Scientific Computing 2008
[16] 웹사이트 Vulnerability in rmtree() and remove_tree(): CVE-2017-6512 https://rt.cpan.org/[...] CPAN 2017-06-05
[17] 학술회의 The Mars Rover Spirit FLASH anomaly https://www.cs.princ[...] IEEE
[18] 웹사이트 An Investigation of Therac-25 Accidents – I http://courses.cs.vt[...] Courses.cs.vt.edu
[19] 웹사이트 Tracking the blackout bug http://www.securityf[...] 2004-04-07
[20] 웹사이트 Thread Safety Analysis – Clang 10 documentation http://clang.llvm.or[...]
[21] 웹사이트 ThreadSanitizer – Clang 10 documentation http://clang.llvm.or[...]
[22] 웹사이트 Helgrind: a thread error detector http://valgrind.org/[...]
[23] 웹사이트 Data Race Detector https://golang.org/d[...]
[24] 웹사이트 Data race benchmark suite https://github.com/L[...] 2019-07-25
[25] 웹사이트 How Brains Race to Cancel Errant Movements https://blogs.discov[...] Discover Magazine 2013-08-07
[26] 학술지 Canceling actions involves a race between basal ganglia pathways
[27] 웹사이트 競合状態(レースコンディション)とは - 意味をわかりやすく - IT用語辞典 e-Words https://e-words.jp/w[...]
[28] 문서 Anomalous behavior due to unexpected critical dependence on the relative timing of events. FOLDOC. (2002) race condition.
[29] 문서 In this idiom, the code first checks a condition, and then acts based on the result of the condition. Yu Lin, et al.. (2013) CHECK-THEN-ACT Misuse of Java Concurrent Collections. 10.1109/ICST.2013.41
[30] 웹사이트 Secure Programming HOWTO https://dwheeler.com[...]
[31] 뉴스 「史上最悪のソフトウェアバグ」ワースト10を紹介(上) | WIRED.jp https://wired.jp/200[...]
[32] 웹사이트 History's Worst Software Bugs | WIRED https://www.wired.co[...]
[33] 웹사이트 ソフトウェアのバグによって6件の重大な放射線事故が引き起こされた「セラック25事故」とは? - GIGAZINE https://gigazine.net[...]



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com