String buffers and IRQL [ドライバ開発]
String buffers and IRQL
http://blogs.msdn.com/doronh/archive/2006/03/03/543140.aspx
嘘訳
RtlString関数のほとんどはPASSIVE_LEVELでしか使えないと、WDKのドキュメントには書いてある。
これがRtlだけではなくCRTもそうではないのは、いくつか理由がある。
*Rtl関数はPAGEableと宣言されている。そのため関数は自分自身でさえも実行できない。
*UNICODE_STRING内のバッファはスタックベースではないなら普通はPagedPoolから確保される。
*NLSのような文字変換テーブルはPAGEableである
NonPagedPoolからバッファを確保するように作ることによって2つ目の弾丸をovercomeできるだろう。
しかし、そうしたとしてもsurrounding infrastructureのために、まったく助けにならない。
You can overcome the 2nd bullet by making sure your buffer allocations come from NonPagedPool, but that doesn't help you very much because the surrounding infrastructure.
しかし、ちょっと待って欲しい。ここにDISPATCH_LEVEL以上でも動くstrlenとsprintfを使ったサンプルがある。
ドライバの中でもっとも一般的に使われる関数はDbgPrintの機能を実装するのに使われる。
which is invoked like this PRINT(2, ("Number of instances %d\n", DevExt->NumInstances")); and this actually works.
以下のサンプルはPRINT(2, ("Number of instances %d\n", DevExt->NumInstances"));とのようにinvokedされ、そして問題なく動く。なぜだろうか?
なぜならformat stringに%sおよびそれに類するformat specifiersが含まれていないからである。
もし、%sを使おうとすると、aforementioned tableをヒットし、潜在的にpaged out pool(とbugcheck)をヒットすることになる
何が起きるのか? PASSIVE_LEVELより高いIRQLでは、デバッグ出力に文字(%sなど)を渡してはいけない。当然、なぜ高いIRQLで文字列を取り扱う必要があるのか良く考えなければならない。
比較やそれに似た操作はしないことを希望する?。
また、文字を使うところでWPPを使うことも考えたほうがいい。(なぜならWPPは文字列をコピーし、後でユーザーモードで処理するからである)
ところどころうまく訳せなかったが、ドライバ内で文字列を扱うときは、
・ドライバ内部の文字列操作は常にPASSIVEであることを前提にしている
付随してASSERT(KeGetCurrentIrql()==PASSIVE_LEVEL)としておくのがいいだろう。
・PASSIVEでない場合には、本当にそこで文字列が必要か考えること(操作はしなくてもいいようにするとか、WPP?を使うなど)
・どーしてもRtl関数をPASSIVEより高いIRQLで使いたい場合は、フォーマット文字列に%sなどの文字列さえ使わなければ動いてくれる。
ドライバのサンプルなどで、よく文字列操作関数を自作しているのを見かけたが、DISPATCH_LEVELなどで動くように自作していたんだろう。
http://blogs.msdn.com/doronh/archive/2006/03/03/543140.aspx
嘘訳
RtlString関数のほとんどはPASSIVE_LEVELでしか使えないと、WDKのドキュメントには書いてある。
これがRtlだけではなくCRTもそうではないのは、いくつか理由がある。
*Rtl関数はPAGEableと宣言されている。そのため関数は自分自身でさえも実行できない。
*UNICODE_STRING内のバッファはスタックベースではないなら普通はPagedPoolから確保される。
*NLSのような文字変換テーブルはPAGEableである
NonPagedPoolからバッファを確保するように作ることによって2つ目の弾丸をovercomeできるだろう。
しかし、そうしたとしてもsurrounding infrastructureのために、まったく助けにならない。
You can overcome the 2nd bullet by making sure your buffer allocations come from NonPagedPool, but that doesn't help you very much because the surrounding infrastructure.
しかし、ちょっと待って欲しい。ここにDISPATCH_LEVEL以上でも動くstrlenとsprintfを使ったサンプルがある。
ドライバの中でもっとも一般的に使われる関数はDbgPrintの機能を実装するのに使われる。
which is invoked like this PRINT(2, ("Number of instances %d\n", DevExt->NumInstances")); and this actually works.
以下のサンプルはPRINT(2, ("Number of instances %d\n", DevExt->NumInstances"));とのようにinvokedされ、そして問題なく動く。なぜだろうか?
なぜならformat stringに%sおよびそれに類するformat specifiersが含まれていないからである。
もし、%sを使おうとすると、aforementioned tableをヒットし、潜在的にpaged out pool(とbugcheck)をヒットすることになる
何が起きるのか? PASSIVE_LEVELより高いIRQLでは、デバッグ出力に文字(%sなど)を渡してはいけない。当然、なぜ高いIRQLで文字列を取り扱う必要があるのか良く考えなければならない。
比較やそれに似た操作はしないことを希望する?。
また、文字を使うところでWPPを使うことも考えたほうがいい。(なぜならWPPは文字列をコピーし、後でユーザーモードで処理するからである)
ところどころうまく訳せなかったが、ドライバ内で文字列を扱うときは、
・ドライバ内部の文字列操作は常にPASSIVEであることを前提にしている
付随してASSERT(KeGetCurrentIrql()==PASSIVE_LEVEL)としておくのがいいだろう。
・PASSIVEでない場合には、本当にそこで文字列が必要か考えること(操作はしなくてもいいようにするとか、WPP?を使うなど)
・どーしてもRtl関数をPASSIVEより高いIRQLで使いたい場合は、フォーマット文字列に%sなどの文字列さえ使わなければ動いてくれる。
ドライバのサンプルなどで、よく文字列操作関数を自作しているのを見かけたが、DISPATCH_LEVELなどで動くように自作していたんだろう。
VOID MyPrint(PSTR Format, ...)
{
CHAR ach[128];
va_list va;
NTSTATUS status;
va_start(va, Format);
status = RtlStringCbVPrintfA(ach, sizeof(ach), Format, va);
if (NT_SUCCESS(status)) { DbgPrint(ach); }
va_end(va);
}
コメント 0