LKMLでパッチをレビューしていて気になったのと、一部の混乱の原因には私も加担していると思っているので、個人用のメモを兼ねてここらでまとめておきたい。適当にメモしたので間違っていたらあとで修正する。
対象となるカーネルは、linux-5.0(-rcX)である。
背景
Linuxカーネルには独自に実装されたstring操作系(特にコピー系の)関数がいくつか存在する。基本的には標準ライブラリから持ってきたもの(カーネルはビルド時に標準ライブラリをリンクできない。標準ライブラリの関数はユーザ空間で実行されることを前提にしている)が多いのだが、OS特有の特権レベルやメモリ空間を意識した拡張関数も存在する。
問題は、これらの実装の中で、実装を見ていくと'\0'つまりNULL終端の扱いが色々と違っているのだ。
strncpy系
strncpy()、strlcpy()、strscpy()、strncpy_from_user()、strncpy_from_unsafe()がある。
- 
strncpy(dst,src,n):srcからdstへ文字列をコピーする。文字列がnより長い場合'\0'はコピーされない。dstを返す。
- 
strlcpy(dst,src,n):srcからdstへ文字列をコピーする。文字列がn-1より長いと'\0'を最後に追加する。srcにある文字列の'\0'を含まない長さを返す。(コピーした長さではない)
- 
strscpy(dst,src,n):srcからdstへ文字列をコピーする。文字列がn-1より長いと'\0'を最後に追加して-E2BIGを返す。コピーした文字列の長さを返す。(常に戻り値がn-1より小さい)
- 
strncpy_from_user(dst,src,n):ユーザ空間にあるsrcからdstへ文字列をコピーする。文字列がnより長い場合'\0'はコピーされない。コピーした文字列の'\0'を含まない長さを返す。ユーザ空間の上限を越える、あるいはページフォルトが起きた場合は-EFAULTを返す。
- 
strncpy_from_unsafe(dst,src,n):カーネル空間にあるsrc(但しアクセスできるとは限らない)からdstへ文字列をコピーする。文字列がn-1より長いと'\0'を最後に追加する。'\0'を含むコピーした文字列の長さ(最大値n)を返す。アクセスに失敗した場合-EFAULTを返す。
strlen系
strlen()、strnlen()、strnlen_user()がある。
- 
strlen(str),strnlen(str,n): '\0'をカウントしないで文字列の長さを返す。nより長い場合はnを返す。
- 
strnlen_user(str,n): '\0'を含むユーザ空間にある文字列の長さを返す。nより長い場合はn+1以上の値を返す。nは'\0'を含む値であること。ユーザ空間の上限を越える、あるいはページフォルトが起きた場合は0を返す。
strdup系
kstrdup()、strndup_user()、memdup_user_null()、kmemdup_null()がある。memdup_null系は文字列の長さが分かっている場合にstrdup系の代わりに使われることが望ましい。
- 
kstrndup(str,n,gpf):メモリを確保し、最大nバイトの文字列をコピーする。文字列がnより長い場合、n文字コピーし、最後に'\0'を追加する。つまり、確保するメモリは最大 n + 1 になる可能性がある。メモリが確保できなければENOMEMを返す。
- 
strndup_user(str,n):メモリを確保し、最大n-1バイトの文字列をコピーする。文字列がn-1より長い場合は-EINVALを返し、ユーザ空間アクセスに失敗した場合は-EFAULTを返す。メモリが確保できなければENOMEMを返す。
- 
kmemdup_null(src,n,gpf):メモリを確保し、nバイトのデータをコピーし、n+1バイト目に'\0'を追加する。メモリが確保できなければENOMEMを返す。
- 
memdup_user_null(src,n):メモリを確保し、nバイトのデータをコピーし、n+1バイト目に'\0'を追加する。ユーザ空間アクセスに失敗した場合は-EFAULTを返し、メモリが確保できなければENOMEMを返す。
番外編
- 
strstarts(str,prefix): strがprefixから始まっていればtrue、そうでなければfalseを返す。
- 
str_has_prefix(stre,prefix): strがprefixから始まっていればprefixの長さを、そうでなければ0を返す。
同じ処理だが戻り値が違う。それだけである。ftraceの中の処理で多用されるパターンで、prefixの長さがわかったほうがいいよね、ということで後者が導入されたんだけど・・・。
まとめ
strn系のカーネル内APIの混乱ぶりが分かっていただけただろうか・・・。strcpy系は0フィルするかどうか書き忘れたけど、多分strcpy以外は0フィルしないだろう。