의미
포인터 연산은 주소에 대해서 연산을 하는 것을 의미하는데, 포인터에 더해지는 값은 자동적으로 포인터가 가리키는 데이터형으로 조정된다.
동작 방식
포인터 연산의 특징
(1) 주소상수 + 정수형 상수(n)
-> 주소 상수 + (n * 주소상수에 해당하는 기억공간의 크기)
(2) 주소상수 - 주소상수
-> 주소에 해당하는 기억공간 간의 첨자차이가 된다.
문제 코드 1. 단축 평가 방식을 신경써서 AND, OR 연산자를 사용하자.
출력을 예상해보기
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUFFER_SIZE 5
int getNumber(int *);
int main()
{
int p;
int buffer[BUFFER_SIZE];
int *bufptr = buffer;
while(bufptr < (buffer + sizeof(buffer)))
{
if(getNumber(&p))
{
*bufptr++ = p;
}
}
for(int i = 0; i < BUFFER_SIZE; i++)
{
printf("buffer[%d] : %d\n", i, *(buffer + i));
}
return 0;
}
int getNumber(int *ptr)
{
char tmp[10];
char *nr, *br;
*ptr = 0;
memset(tmp, 0, sizeof(tmp));
printf("정수 입력 : ");
while(1)
{
if(fgets(tmp, sizeof(tmp), stdin) == NULL)
{
return 0;
}
/* 예외 처리 */
// fgets로 받은 문자에 반드시 개행문자가 있다고 가정하지 않기
nr = strchr(tmp, '\n');
if(nr != NULL) *nr = '\0';
else fflush(stdin);
if(strlen(tmp) == 0 || strlen(tmp) > 3)
{
return 0;
}
// 문자가 없거나 중간에 숫자가 아닌 문자가 존재하는지 확인하기
*ptr = (int)strtol(tmp, &br, 10);
if(br == tmp || *br != '\0')
{
return 0;
}
break;
}
return 1;
}
출력 :
정수 입력을 5번이 아닌 20번을 받아야 프로그램이 종료되게 된다. 그 이유가 무엇일까?
취약점 분석 및 해결 코드 1
분석
(1) 포인터 연산의 특징 1번이 그 이유이다. 주소상수 + 정수형 상수(n) = 주소상수 + (주소상수에 해당하는 기억공간의 크기 * n)이 된다.
-> 즉, 위 코드에서 while(bufptr < (buffer + sizeof(buffer))) 이 부분에서 buffer + sizeof(buffer)은 buffer + (20)이 되고 buffer + (sizeof(int) * 20)이 되면서 원하는 버퍼사이즈인 5번이 아닌 20번을 반복하게 된다.
아래 간단하게 수정된 코드를 보고 확실히 짚고 넘어가도록 하자.
해결 방법 : 포인터 연산을 실행할 때 포인터 연산 특징을 염두해 두자. ( 굉장히 많이 사용되기 때문에 특히 더 신경쓰자 )
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUFFER_SIZE 5
int getNumber(int *);
int main()
{
int p;
int buffer[BUFFER_SIZE];
int *bufptr = buffer;
while(bufptr < (buffer + BUFFER_SIZE))
{
if(getNumber(&p))
{
*bufptr++ = p;
}
}
for(int i = 0; i < BUFFER_SIZE; i++)
{
printf("buffer[%d] : %d\n", i, *(buffer + i));
}
return 0;
}
int getNumber(int *ptr)
{
char tmp[10];
char *nr, *br;
*ptr = 0;
memset(tmp, 0, sizeof(tmp));
printf("정수 입력 : ");
while(1)
{
if(fgets(tmp, sizeof(tmp), stdin) == NULL)
{
return 0;
}
/* 예외 처리 */
// fgets로 받은 문자에 반드시 개행문자가 있다고 가정하지 않기
nr = strchr(tmp, '\n');
if(nr != NULL) *nr = '\0';
else fflush(stdin);
if(strlen(tmp) == 0 || strlen(tmp) > 3)
{
return 0;
}
// 문자가 없거나 중간에 숫자가 아닌 문자가 존재하는지 확인하기
*ptr = (int)strtol(tmp, &br, 10);
if(br == tmp || *br != '\0')
{
return 0;
}
break;
}
return 1;
}
POINT
- 주소상수 + 정수형 상수(n) : 주소 상수 + (n * 주소상수에 해당하는 기억공간의 크기)
- 주소상수 - 주소상수 : 주소에 해당하는 기억공간 간의 첨자차이가 된다.
참조
EXP08-C. Ensure pointer arithmetic is used correctly
wiki.sei.cmu.edu/confluence/display/c/EXP08-C.+Ensure+pointer+arithmetic+is+used+correctly
'C, C++ > 표현식' 카테고리의 다른 글
[CERT C/표현식] (5) NULL 체크의 중요성 (0) | 2021.02.03 |
---|---|
[CERT C/표현식] (4) 함수 반환 값의 중요성 (0) | 2021.01.29 |
[CERT C/표현식] (2) 단축 평가 방식의 중요성 (0) | 2021.01.28 |
[CERT C/표현식] (1) 연산자 우선순위의 중요성 (0) | 2021.01.25 |