なんとなくLinuxカーネルのライブラリにあるstrncpy_from_user()を読んでみます。
ファイルはlib/strncpy_from_user.cです。
関数名から何をするかは想像つきますね。関数はこうなってます。
99 long strncpy_from_user(char *dst, const char __user *src, long count) 100 { 101 unsigned long max_addr, src_addr; 102 103 if (unlikely(count <= 0)) 104 return 0; 105 106 max_addr = user_addr_max(); 107 src_addr = (unsigned long)src; 108 if (likely(src_addr < max_addr)) { 109 unsigned long max = max_addr - src_addr; 110 return do_strncpy_from_user(dst, src, count, max); 111 } 112 return -EFAULT; 113 } 114 EXPORT_SYMBOL(strncpy_from_user);
最初にアドレスのチェックがあります。まず、max_addrは何になるのかということでuser_addr_max()を見てみます。arch/x86/include/asm/uaccess.hでこのようなマクロとして定義されています。
36 #define user_addr_max() (current_thread_info()->addr_limit.seg)
segはmm_segment_t構造体のメンバ変数です。この構造体はarch/x86/include/asm/processor.hで定義されています。
486 typedef struct { 487 unsigned long seg; 488 } mm_segment_t;
で、実際どんな値が入っているのかを↓だけ実行するカーネルモジュールを作って見てみると、
pr_info("limit -> 0x%lx\n", current_thread_info()->addr_limit.seg);
このようになります。
[ 6494.031450] limit -> 0x7ffffffff000
linuxのx86_64の場合、ユーザランドのアドレス空間はDocumentation/x86/x86_64/mm.txtにて下記のようになると書かれています。
6 0000000000000000 - 00007fffffffffff (=47 bits) user space, different per mm
そして、うちの環境では1ページは4KiBなので、0x7ffffffff000はユーザランドのアドレス-1ページ分という感じですね
masami@saga:~$ getconf PAGE_SIZE 4096
ということで最初のチェックは、コピー元のアドレスが変な場所にないかをチェックしています。
実際に文字列をコピーするのはdo_strncpy_from_user()ですね。
4番目のmaxはunsigned long max = max_addr - src_addr;で決まった値になります。
23 static inline long do_strncpy_from_user(char *dst, const char __user *src, long count, unsigned long max)
最初にコピーするサイズを決めます。
24 { 25 const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; 26 long res = 0; 27 28 /* 29 * Truncate 'max' to the user-specified limit, so that 30 * we only have one limit we need to check in the loop 31 */ 32 if (max > count) 33 max = count; 34
アドレスがsizeof(long) - 1の境界にアライメントされてるかのチェックです。これはCONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESSがyならチェックされます。多分大抵のディストリビューションならyになってるんじゃないかと。longの境界に無い場合はbyte_at_a_timeラベルに飛びます。
35 if (IS_UNALIGNED(src, dst)) 36 goto byte_at_a_time; 37
ここからlongのサイズごとにコピーが行われます。
38 while (max >= sizeof(unsigned long)) { 39 unsigned long c, data; 40 41 /* Fall back to byte-at-a-time if we get a page fault */ 42 if (unlikely(__get_user(c,(unsigned long __user *)(src+res)))) 43 break; 44 *(unsigned long *)(dst+res) = c;
has_zero()はarch/x86/include/asm/word-at-a-time.hにあります。cに0が含まれている場合に0以外の値が返ります。なのでhas_zero()が真になる場合はNULL文字が含まれている場合ということなので、コピーがここで終了となりますね。
45 if (has_zero(c, &data, &constants)) { 46 data = prep_zero_mask(c, data, &constants); 47 data = create_zero_mask(data); 48 return res + find_zero(data); 49 } 50 res += sizeof(unsigned long); 51 max -= sizeof(unsigned long); 52 } 53
byte_at_a_timeラベルのほうは素直に1バイトずつのコピーです。
54 byte_at_a_time: 55 while (max) { 56 char c; 57 58 if (unlikely(__get_user(c,src+res))) 59 return -EFAULT; 60 dst[res] = c; 61 if (!c) 62 return res; 63 res++; 64 max--; 65 } 66 67 /* 68 * Uhhuh. We hit 'max'. But was that the user-specified maximum 69 * too? If so, that's ok - we got as much as the user asked for. 70 */ 71 if (res >= count) 72 return res; 73 74 /* 75 * Nope: we hit the address space limit, and we still had more 76 * characters the caller would have wanted. That's an EFAULT. 77 */ 78 return -EFAULT; 79 }
ちなみに、マジックワードの定番ともいえる0xdeadbeefがinclude/linux/kernel.hにあります。
45 #define STACK_MAGIC 0xdeadbeef
ふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道
- 作者: 青木峰郎
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2005/07/27
- メディア: 単行本
- 購入: 35人 クリック: 450回
- この商品を含むブログ (148件) を見る