LoginSignup
25
26

More than 5 years have passed since last update.

Linuxカーネルにおけるstrn系関数の挙動のまとめ

Last updated at Posted at 2019-02-19

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フィルしないだろう。

25
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
26