Secure Coding in C - 정수 변환, overflow, sizeof, limits.h, stdin.h, inttypes.h stdin, bit 연산

markyang92·2021년 6월 18일
0

Secure Coding in C

목록 보기
3/7

정수 변환 규칙

  • Misra Dir 4.6
    • 기본 수치 유형 대신 크기와 부호가 표시된 typedef를 사용해야한다.
    • C99에서 stdint.h에서 제공하는 유형을 사용해야 한다.

정수 변환 규칙, Promotion

  • explicit convsersion: 변환 연산자를 사용한 변환
  • implicit convsersion: 특정 연산에 의한 변환

  • int보다 작은 정수 타입은 연산이 수행될 때!! int, unsigned int로 변환
  • 오버플로로 인한 산술 연산 에러 방지하기 위함

  1. bit 수가 많은 타입이 높은 순위 가진다.
  2. 각기 다른 signed 정수는 순위가 다르다.
  3. signed 정수 타입의 순위 자신보다 정밀도가 낮은 다른 signed보다 순위 높음
    • long long int > long int > int > short int > signed char
    • unsigned long long int > unsigned long int > unsigned short int > unsigned char

  • 이항 연산의 경우 두 피연산자는 같은 타입으로 변환된다.
  • 두 개의 피연산자가 같은 타입이면 변환하지 않는다.
  • 두 개의 피연산자가 같은 정수 타입이면 범위가 큰 타입으로 변환된다.
  • unsigned 정수 타입의 피연산자가 다른 피연산자의 순위보다 크거나 같은 경우, signed 정수 타입의 피연산자는 unsigned 정수 타입으로 변환된다.
    • 특히 unsigned int + int 할 때 주의!
  • signed 정수 타입의 피연산자가 unsigned 정수 타입의 모든 값을 표현할 수 있는 경우, unsigned 정수 타입의 피연산자는 signed 정수 타입으로 변환된다.
  • signed 정수 타입의 피연산자가 unsigned 정수 타입의 모든 값을 표현할 수 있는 경우, signed 정수 타입의 피연산자는 동일 타입의 unsigned 타입으로 두 피연산자 모두 변환된다.

1. 부적절한 코드

  • 서로 다른 타입에 대해 연산을 수행할 경우 주의

1. 해결방법

  • 타입 일치


오버플로우

unsigned: wrapping 주의

  • C99
    • unsigned 피연산자를 사용한 계산은 결코 오버플로가 발생하지 않는다. -> 대신 원하지 않는 값 래핑
      • 값이 저장될 정수 타입으로 표현될 수 없는 경우, 나머지 연산으로 값을 줄여(wrap around) 표현하기 때문이다.
        • 정수 래핑이라고한다.
      • 따라서, 신뢰할 수 없는 src 로 부터 얻어진 정수 값이 아래와 같은 곳에 사용되면 절대 래핑을 허용해서는 안된다.
        1. 배열 인덱스
        2. 포인터 연산의 일부
        3. 루프 카운터
        4. 메모리 할당 함수의 인자
        5. 그 밖의 보안에 민감한 코드

warpping 방지 코드

  • 다음과 같은 코드를 넣어서 warapping 방지

signed :오버플로X 보장하기

  • C에서 정수 오버플로는 정의되지 않은 동작이다. 신뢰할 수 없는 src로 부터 얻은 값에 대한 연산이 아래에 사용되면 XX!!
    1. 배열 인덱스
    2. 포인터 연산의 일부
    3. 루프 카운터
    4. 메모리 할당 함수의 인자
    5. 그 밖의 보안에 민감한 코드

'+' overflow 방지

  • 다음의 코드를 넣어 signed 연산 overflow 방지하자

'*' overflow 방지

  • signed 정수의 곱셈 연산은 정수 오버플로우가 일어날 수 있기 때문에 다음의 코드는 의도하지 않는 결과를 초래할 수 있다.
  • 오버플로우 발생 전 미리 테스트

'/' overflow 방지

  • signed 정수의 나눗셈 연산
    • 피제수최소값(INT_MIN)
    • 제수음수일 때
      : 2's complement 표기에서 정수 오버플로가 일어날 수 있다.


해결 방법

  • 오버플로우가 발생하기 전에 미리 테스트

'/' floating point

  • overflow는 아니지만 나누어 떨어 지지 않을 때!!! 부동 소수점 변수에 값을 할당한 경우 정보 손실
  • 표현식의 정수 중 하나를 부동 소수점 타입으로 변환해 피할 수 있다.

1. 부적절한 코드

  • 정수 타입의 연산 결과는 항상 정수
  • 부동 소수점 결과가 나왔을 경우, 소수 부분은 잘려 나갈 수 있다.


    그냥 나누면 오답!

1-1. 해결 방법

  • 피연산자 하나를 부동 소수점으로 표기


1-2. 해결 방법

  • 정수 값을 부동소수점 변수에 저장하여 처리


'%': 나머지가 양수 가정 X

  • C99
    • 나눗셈의 결과는 첫 번째 피연산자두 번째 피연산자나눈 몫이다.
      나머지 연산자의 결과는 나머지다.
    • 두 연산에서 두 번째 연산자가 0인 경우, 동작은 지정되어 있지 않다.
    • 정수가 나눠질 때, 나머지 연산자의 결과는 수학적인 몫으로 소수점 이하의 값은 버려진다.
      a/b의 몫이 표현 가능하다면 표현식 (a/b)*b+a%b는 a와 동일하다.
  • 버려지는 나머지의 소수점 이하 부분을 '0쪽으로 잘림(truncation toword zero)'라고 한다.
    • C99에서 결과의 부호는 피제수(첫 번째 피연산자)의 부호를 따른다.
      즉, 피제수가 signed int 이고, 음수라면 나머지도 - 부호를 가질 수 있다.

해결 방법

  • 다음의 코드에서 frontrearsigned 정수로 구현 -> 항상 양수임을 보장할 수 없다.
    • 나머지 연산 결과 음수가 될 수 있으며 결과적으로 매우 잘못된 메모리 참조가 발생할 수 있다.
  • unsgined타입을 사용해 문제를 해결하자!

sizeof 문제: arch別

  • 아키텍처별로 int의 실제 Byte할당 문제
  • {,s}ize_t 활용하자.
    • size_t: unsigned long
    • ssize_t: long
Data TypeiAPX86IA-32IA-64SPARC-64ARM-32Alpha64-bit Linux, FreeBSD, NetBSD, OpenBSD
char8888888
short16161616161616
int16323232323232
long32323264326464
long longN/A646464646464
Pointer16/32326464326464

limits.h

  • limits.h는 파일 안에 모든 일치하는 플랫폼에 대한 표준 정수 타입의 범위를 결정하고 매크로를 가지고 있다.
    • UNIX_MAX: unsigned int 가 가질 수 있는 최댓 값
    • LONG_MIN: long int 가 가질 수 있는 최소 값

stdint.h

uintmax_t

  • 코드를 사용하는 Archi에서 unsigned int가 담을 수 있는 최대값의 자료형

1. 부적절한 코드

  • unsigned int 두 개를 곱할 때 모든 비트가 유지될 것이라고 가정하고 있다.
  • 그러나 이코드는 64bit Linux에서는 정상작동하지만, Windows에서는 실패할 수 있다.

1. 해결방법

  • 아주 큰 타입 사용

inttypes.h

  • 아키텍처別 int 제공
    • intptr_t
    • uint64_t

bit 연산

  • bit 연산: ~, >>, <<, |, ^
    1. int 자료형 정수에 bit 연산 피할 것
    2. signed 정수에 대한 비트 연산 구현마다 다르게 정의
    3. 따라서.. unsigned !!!! 정수 일 때만 비트 연산자 사용하자.
      • unsigned int>> << bit shift 연산 시, 0으로 채워짐
  • C99 표준에 따르면 만일 int 타입으로 승계될 때, 원래 타입의 모든 값을 표현할 수 있다면 값은 int로 반환되며 그렇지 않은 경우 unsigned int로 변환된다.

1-1. 1번 int 일 때 비트연산 지양의 예:

  • 비트 필드에서 int bit field를 지정하면 구현에 따라 그 값(-1 || 255)이 달라 질 수 있다.

1. 해결 방법

  • 부호의 유무를 명시적으로 선언하면 의도가 명확해진다.

1-2. 1번 int 일 때 비트연산 지양의 예

  • int 일 때, bit 연산 지양



1-2. 해결 방법

  • unsigned 정수를 사용하면 비어 있는 비트가 0으로 채워지게 되므로 오버 플로우 발생 XX


음수나 피연산자의 비트보다 더 많은 비트 시프트 금지

  • 오른쪽 피연산자의 값이 음수 || 승계된 왼쪽 피연산자의 값과 같거나 큰 경우 undefined behaviour

1. 부적절한 코드


1. 해결 방법

  • 오른쪽 피연산자음수인지? 검사한다.


2. 부적절한 코드

  • 피연산자의 비트보다 더 많은 비트를 시프트하는 것은 undefined expected



2. 해결 방법

  • 시프트 연산 수행 전 검사 수행


profile
pllpokko@alumni.kaist.ac.kr

0개의 댓글