warning: ‘strcpy’ writing one too many bytes into a region of a size that depends on ‘strlen’
警告:strlen()で確保した領域は終端文字’¥0’の考慮が足りない
[-Wstringop-overflow=]
■この記事の概要
この記事では、
C言語でmalloc(strlen)を使う際、
終端文字(ヌル文字)を考慮しないと
メモリ破壊が起こる問題について解説しています。
strcpy関数を使用した自作の具体例や、
適切な修正方法が紹介しています。
■strdupの基本的な使い方
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (int argc,char *argv[]) {
char *my_argv[100] ;
for(int i = 0; i < argc; i++){
char buf[1024];
sprintf(buf,"argv[%d] = %s",i,argv[i]);
my_argv[i] = strdup(buf);
}
for(int i = 0;i < argc; i++){
puts(my_argv[i]);
}
}
./a.out 1 23 456 7890
argv[0] = ./a.out
argv[1] = 1
argv[2] = 23
argv[3] = 456
argv[4] = 7890
コマンドラインのargvを
sprintf で編集し、
出力の文字列をstrdupで複写し
my_argv[]に引っかけています。
このサンプルでは、以下2点の処理が不足しています。
(1) strdupの戻り値のNULLチェック
(2) strdupで確保した領域の解放
■NULLチェックと解放処理を忘れない
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (int argc,char *argv[]) {
char *my_argv[100] ;
for(int id = 0; id < argc; id++){
char buf[1024];
sprintf(buf,"argv[%d] = %s",id,argv[id]);
char *cp = strdup(buf);
if(cp == NULL){ //NULLチェックをする
perror(NULL);
exit(1);//解放はOSに任せるC-FAQ7.24
}
my_argv[id] = cp ;
}
for(int id = 0;id < argc; id++){
puts(my_argv[id]);
free(my_argv[id]);//解放する
}
}
NULLチェックと解放処理を追加した例です。
strdup()は中でmalloc()を呼んでいるので
malloc()に失敗するとNULLを返します。
又、
解放しないとメモリリークしてしまいます。
■strdupを自作する場合の注意事項+1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *NG_strdup(char *cp){
char *mp = malloc(strlen(cp)) ;
if(mp == NULL){
return NULL;
}
return strcpy(mp,cp);
}
char *OK_strdup(char *cp){
char *mp = malloc(strlen(cp)+1) ;
if(mp == NULL){
return NULL;
}
return strcpy(mp,cp);
}
int main (void) {
printf("OK=%s\n",OK_strdup("OK:終端文字考慮"));
printf("NG=%s\n",NG_strdup("NG:終端文字分忘れ"));
}
$ gcc -Wall -Wextra f3-my_strdup.c -fsanitize=address
$ ./a.out
OK=OK:終端文字考慮
中略
==12650==ERROR: AddressSanitizer:
heap-buffer-overflow on address
後略
strdupを自作する場合は
malloc(strlen(func)+1)の1を忘れないで下さい。
忘れると終端文字\0の分が無いのでメモリ破壊が発生します。
実行しても異常終了しない場合は
問題が潜在化しているので
gcc -fsanitize=address -g
でコンパイル実行してみて下さい。
■malloc使用時しなくて良い事
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *KISS_strdup(char *cp){
size_t len = strlen(cp);
char *mp = (char *)malloc(sizeof(char) * (len + 1)) ;
if(mp == NULL){
return NULL;
}
strcpy(mp,cp);
return mp;
}
int main (void) {
char *cp = KISS_strdup("Keep it simple stupid.");
printf("KISSの原則=%s\n",cp);
free(cp);
}
(1) (char *)mallocと戻り値をキャストする必要はありません。
#include <stdlib.h>を忘れてキャストすると事故る場合があります。
コンパイルエラーになってしまう人は多分C++コンパイラを起動しています。
C++を気にする人はnewを使いましょう。
(2)sizeof(char)はC言語仕様で1と決まっているので明示的に掛け算する必要はありません。
参考:
http://www.kouno.jp/home/c_faq/c7.html#7
http://www.kouno.jp/home/c_faq/c7.html#8