728x90
반응형
목차
1. Mathworks 계정 생성 및 라이선스 설정
2. Polyspace 초기 화면 및 Code Prover 기본
3. 최대값 출력 프로그램 작성
  - 메모리 동적 할당
4. 프로그램 Code Prover 분석
  1) Non-initialized local variable
  2) Illegally dereferenced pointer
  3) Non-initialized variable


1. Mathworks 계정 생성 및 라이선스 설정

https://kr.mathworks.com 

 

MathWorks - MATLAB 및 Simulink 개발 회사

MathWorks Korea의 공식 홈페이지로서, MATLAB(매트랩) 및 Simulink(시뮬링크) 제품 및 서비스, 다운로드, 교육 및 강좌, 커뮤니티 등 다양한 정보를 제공합니다.

kr.mathworks.com

Mathworks 라이선스 설정

우측 상단 아이콘 - 내 계정 > 내 소프트웨어

다운로드 아이콘 클릭하여 R2023a 릴리스 버전 다운로드(계정 내 라이선스 필요)

R2023a 버전 installer 실행

대상 폴더: /Applications/Polyspace/R2023a

제품 선택: Polyspace Bug Finder + Polyspace Code Prover

필요 용량: 3.72 GB 정도


2. Polyspace 초기 화면 및 Code Prover 기본

terminal 연결하여, Polyspace 설정 경로로 이동 후, 실행

cd /Applications/Polyspace/R2023a/polyspace/bin
./polyspace &

실행 완료되면 하단 Polyspace R2023a 연결

Run-time Check 중

Red Check: 특정 에러가 100%로 발생하는 경우

Gray Check: Dead code, Unreachable code

Orange Check: 특정 에러에 대해 증명할 수 없는 경우

Green Check: 런타임 에러가 발생하지 않는 경우(정상)

Purple Category: Coding rules


3. 최대값 출력 프로그램 작성

arr 배열에 대한 초기값을 넣어주지 않아 에러 발생

// 초기 코드
#include <stdio.h>

int main(int argc, const char * argv[]) {
    int n, arr[n];
    
    printf("Enter full array size\n");
    scanf("%d", &n);
    
    printf("Enter values to store in the array\n");
    for (int i = 0; i < n; i++) {
        scanf("%d", &arr[i]);
    }
    
    int max = arr[0];
    for (int j = 1; j < n; j++) {
        if (max < arr[j])
            max = arr[j];
    }
    printf("The maximum value of an array element is %d\n", max);
    return 0;
}

arr 배열을 만들기만 하고 값을 넣어주지 않음 – 선언(Declare)의 상태

선언한 변수에 처음 값을 넣어 줌 – 초기화(Initialize)

초기화한 이후 변수에 값을 넣어 줌 – 할당(Allocaiton)

→ arr 배열 동적 할당 필요

메모리 동적 할당

컴퓨터 프로그램이 실행되는 런타임 도중 사용할 메모리 공간을 할당

동적 할당되는 메모리는 힙 영역에 생성

sizeof(int) 4byte 할당 * n

이후 동적 할당 메모리 해제(free)

// sol1
#include <stdlib.h>	// malloc, free 함수가 포함된 헤더 파일
#include <strings.h>	// memset 함수가 포함된 헤더 파일

int* arr = (int *)malloc(sizeof(int) * arr_size);
memset(arr, 0, sizeof(int) * arr_size);

free(arr);

malloc 할당 이후 memset 초기화하는 과정은 calloc 함수 하나로 대체 가능

// sol2
#include <stdlib.h>	// calloc 함수가 포함된 헤더 파일

int* arr = (int *)calloc(0, sizeof(int) * arr_size);

// 1차 수정
//
//  main.c
//  week.04_SecureCoding
//
//  Created by sehee on 2023/03/27.
//

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

int max(int* ptr, int n);

int main(int argc, const char * argv[]) {
    // 최대값을 출력하는 프로그램, 배열은 동적 할당 받기
    int arr_size;
    
    // 첫번째 입력: 전체 배열 크기
    printf("Enter full array size\n");
    scanf("%d", &arr_size);
    int* arr = (int *)malloc(sizeof(int) * arr_size);
//    memset(arr, 0, sizeof(int) * arr_size);
    
    // error
    if (arr == NULL) {
        printf("malloc error");
        exit(1);
    }
    
    // 두번째 입력: 배열 내 저장할 함수 - 각 index에 해당 값 할당
    printf("Enter values to store in the array\n");
    for (int i = 0; i < arr_size; i++) {
        scanf("%d", &arr[i]);
    }
    
    // 출력: 최대값
    printf("The maximum value of an array element is %d\n", max(arr, arr_size));
    free(arr);
    return 0;
}

int max(int* ptr, int n) {
    int max_val = ptr[0];
    for (int i = 1; i < n; i++) {
        if (max_val < ptr[i]) {
            max_val = ptr[i];
        }
    }
    return max_val;
}

4. 프로그램 Code Prover 분석

1) Non-initialized local variable

초기 변수(arr_size) 선언 후 초기화

int arr_size = 0;

2) Illegally dereferenced pointer

3) Non-initialized variable

max 함수의 ptr에 대해 초기화하지 않음(의미X) - 변경X

// type1
int max(int* ptr, int n);
// type2
int max(int n[], int size);

// 최종 코드
//
//  main.c
//  week.04_SecureCoding
//
//  Created by sehee on 2023/03/27.
//

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

int max(int n[], int size);

int main(int argc, const char * argv[]) {
    // 최대값을 출력하는 프로그램, 배열은 동적 할당 받기
    int arr_size = 0;
    
    // 첫번째 입력: 전체 배열 크기
    printf("Enter full array size\n");
    scanf("%d", &arr_size);
    int* arr = (int *)malloc(sizeof(int) * arr_size);
//    memset(arr, 0, sizeof(int) * arr_size);
    
    // error
    if (arr == NULL) {
        printf("malloc error");
        exit(1);
    }
    
    // 두번째 입력: 배열 내 저장할 함수 - 각 index에 해당 값 할당
    printf("Enter values to store in the array\n");
    for (int i = 0; i < arr_size; i++) {
        scanf("%d", &arr[i]);
    }
    
    // 출력: 최대값
    printf("The maximum value of an array element is %d\n", max(arr, arr_size));
    free(arr);
    return 0;
}

int max(int n[], int size) {
    int max_val = n[0];
    for (int i = 1; i < size; i++) {
        if (max_val < n[i]) {
            max_val = n[i];
        }
    }
    return max_val;
}

 

728x90
728x90
728x90
반응형
목차
1. char 자료형
  - limits.h 헤더 파일

2. scanf 오류
3. int 자료형
4. 이진탐색(Binary Search) 시 발생하는 오버플로우 대안

char 자료형

C언어에서는 정수 자료형인 char를 이용하여 문자 한 개를 저장함

이 때, char 자료형의 표현 범위는 -128~127이며, char에 문자를 저장할 때는 문자 자체를 저장하는 것이 아니라 문자에 해당하는 정수 값을 저장(ASCII)

(00000000~1111111까지 256가지 중 127까지 양수, 128부터 음수로 표현)

따라서, char로 선언한 변수에 ASCII 코드값 128을 저장한다면, 이에 overflow가 발생하여 -128이 됨

#include <stdio.h>
#include <limits.h>

int main() {
    printf("char min: %d, char max: %d\n", CHAR_MIN, CHAR_MAX);
    // char min: -128, char max: 127
    return 0;
}


scanf 오류가 발생하는 이유

보안 문제, 프로젝트에서 SDL(Security Development Lifecycle) 자동 검사를 수행하기 때문

문제: 문자열이나 파일에 대한 버퍼나 스택 등 메모리에 문제가 생길 수 있음

해결 방법

1) 코드 최상단에 #define _CRT_SECURE_NO_WARNINGS 추가

  - scanf 보안 경고로 인한 컴파일 에러 방지

2) scanf → scanf_s로 변경(visual studio에서만 호환 가능)

  - 메모리의 사이즈를 요구하는 함수(_s), C언어 표준 함수는 아님

3) scanf_s("%s", sentence, sizeof(sentence))라 명시(마지막에 sentence 사이즈)

(e.g. scanf_s(“%d”, &store, 10); → [10]개의 사이즈를 받는다고 명시)

 

[참고]

sscanf()란? 입력 대상: 표준 입력(X), 매개변수로 전달되는 문자열 버퍼(O)

fscanf()란? 입력 대상: 파일(f) 스트림에서 포맷 스트링에 맞게 데이터를 읽어들임


int 자료형

int 자료형의 표현 범위는 -2147483648~2147483647이며,

2,147,483,628 = 2³¹

따라서 부호 있는 32bit 정수가 보유할 수 있는 최대값은 2³¹-1 (int 범위: -2³¹ ~  2³¹-1)

#include <stdio.h>
#include <limits.h>

int main() {
    printf("int min: %d, int max: %d\n", INT_MIN, INT_MAX);
    // int min: -2147483648, int max: 2147483647
    return 0;
}


이진탐색(Binary Search)  시 발생하는 오버플로우 대안

first = 0;
last = n - 1;
middle = (first + last)/2;

부호 있는 32bit 정수가 보유할 수 있는 최대값이 2³¹-1임

이 때, 검색할 요소가 2³¹-2 (마지막에서 두번째 요소)라고 가정해 보자

 

[0, 1, 2, 3, … , 2³¹/2, …, (2³¹-3), (2³¹-2), (2³¹-1)]

 

첫 번째 반복 시 middle = (0 + (2³¹-1))/2

target은 해당 배열의 우측에 위치하므로,

first = middle + 1 = (2³¹-1)/2 + 1 = 2³⁰ + 1/2

(*)두 번째 반복 시, middle = (first + last)/2 = {(2³⁰ + 1/2) + (2³¹ - 1)}/2 = {(2³⁰ + 2³¹ - 1/2}/2 = 2²⁹+2³⁰-1/4 = 2²⁹(1+2) – 1/4 = 2³⁰ + 2²⁹ – 1/4 (= 2²⁹ * 3 – 1/4)

이 때 first = 2²⁹ * 3 – 1/4 + 1 = 2²⁹ * 3 + 3/4

세 번째 반복 시, middle = (first + last)/2 = {(2²⁹ + 2³⁰ + 3/4) +(2³¹ - 1)}/2 = 2³⁰ + 2²⁹ + 2²⁸ - 1/8

 

해당 함수를 n 번 반복 시, n번 째 middle = (2³⁰ + 2²⁹ + 2²⁸ + … + 2^(30-n)) - 1/2^(n)

원하는 값(2³¹-2)을 구하기까지 middle은 최대 2³¹을 넘지 않으므로 문제되지 않음

(∵ (2³⁰ + 2²⁹ + 2²⁸ + … + 2¹) = 2³¹ - 1 < 2³¹)

 

하지만 두 번째 연산 시,(*) first와 last의 합이 INT_MAX인 2³¹ - 1을 넘어가게 되어 오버플로우 발생

➡️ middle이 음수가 됨

 

따라서, middle 값을 다음과 같이 변경하여 문제 해결 가능

문제: first와 last의 합이 2³¹을 초과하게 되어 overflow 발생

기존 연산: middle = (first + last)/2;

변경 연산1: middle = first + (last – first)/2;

변경 연산2: middle. = ((unsigned int)first + (unsigned int)last)) >> 1

 

* unsigned int는 sign(부호)가 없는 정수형으로,

Overflow된 값을 정상적인 값으로 판단하여 unsigned 연산에서 고의적으로 wrap around 이용

 

[참고자료]

The curious case of Binary Search - The famous bug that remained undetected for20 years

https://thebittheories.com/the-curious-case-of-binary-search-the-famous-bug-that-remained-undetected-for-20-years-973e89fc212

 

The curious case of Binary Search — The famous bug that remained undetected for 20 years

Nearly all Binary Search and Merge Sort implementations are broken!

thebittheories.com

 

728x90
728x90

+ Recent posts