無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

snprintf


snprintf()はスタックに積まれた引数のトップアドレスを取得し、第一引数のfmtから動的に、引数タイプを取得することで、関数引数に依存しないsprintfです。
#include <linux/module.h>

MODULE_LICENSE("GPL");

int init_module(void)
{
       char    buff[64];

       snprintf(buff, sizeof(buff), "%s:%d\n", "hoge", 1);
       printk(buff);
       snprintf(buff, sizeof(buff), "%d:%d:%s\n", 1,2,"hoge");
       printk(buff);

       return -1;
}

[root@localhost lkm]# insmod snprintf.ko
insmod: error inserting 'snprintf.ko': -1 Operation not permitted
[root@localhost lkm]# dmesg
[49779.685782] hoge:1
[49779.685813] 1:2:hoge
va_start()でfmtのアドレスをargsに取得します。引数はスタックの上位に向かって順に配置されていきます。従ってspの配下(最初にpush bpされ、それを考慮してのsp)のアドレスのfmtのアドレスを取得し、そのサイズ分を加算したアドレスが、以降編集対象引数の先頭アドレスとなります。

引数のスタックへのアセンブラでの設定は、型に依存せず、acpi_native_intのサイズ単位で行われ、システムによっては4バイト/8バイトとなります。それが8バイトとすると、32ビットカーネルだと、fmtのアドレスにポインタサイズの4加算しても、実際は8バイトで、次の引数のアドレスではありません。_bnd()はポインタサイズにacpi_native_intサイズに応じて、そのサイズ単位で切り上げする事で、システムに依存しない形で、アドレスを取得できるようにしています。

vsnprintf()はfmtから、順に引数タイプを取得し、Tの取得タイプ引数でva_arg()展開し、取得したアドレスをbuffに設定します。同時に次の引数にアドレスを更新します。

va_end()は意味不明です。gccに最適化に影響するのかもしれませんが、実装からするとまったく意味ありません。推測ですが、va_arg()で取得し終えたva_listアドレスは、呼び出し側のスタック領域となり、プログラミング的に、脆弱性とならない旨のコーダへの注意勧告じゃないでしょうか?
typedef unsigned int            u32;
typedef unsigned long long      u64;

typedef char *va_list;

typedef s32 acpi_native_int;
typedef s64 acpi_native_int;

#define  _AUPBND                (sizeof (acpi_native_int) - 1)
#define _bnd(X, bnd)            (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_end(ap)              (void) 0
#define va_start(ap, A)         (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
#define va_arg(ap, T)           (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))

int snprintf(char *buf, size_t size, const char *fmt, ...)
{
       va_list args;
       int i;

       va_start(args, fmt);
       i = vsnprintf(buf, size, fmt, args);
       va_end(args);

       return i;
}

追記

カーネルと直接関係ないCプログラミングマターですが、#define _AUPBND/#define _bnd(X, bnd)の実装が気になって調べてみました。、ビット演算による、区切り単位の切上げ実装には、and/or/xor/notたかがビット演算、されどビット演算の思いです。


最終更新 2015/04/21 19:39:05 - north
(2015/04/21 19:39:05 作成)