次の環境のプロセスがあります。
root@a-vm:/proc/1363# hexdump -C environ
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
0000016c
私はこのようなことを見たことがありません。environ
null で終わるkey=value
ペアが含まれると予想されるため、この出力はさまざまな主張に違反します。既知のカーネルバグを見ていますか?それともUnix / Linuxでこれを行うための合法的な方法はありますか? (…それではなぜ?カーネルがこういうわけではないことを許すのはなぜですか?)
(Linuxでは3.13.0 / Ubuntu Trusty)
(プロセスがいくつかの一時出力を正しい場所に書き込めない理由を特定しようとしましたが、この問題に遭遇しました。一時保存に特定のディレクトリを使用している必要があり、環境変数を設定してそのディレクトリに関する情報を受け取りました。TMP
; しかし、私はTMP
多くのnull値ではなく、非常に平凡に見えるパスを設定しており、完全に空の環境を見たことがありません。
答え1
たわごとではありません。 Linuxにはこれを行うための合法的な方法がありますが、あなたの期待は間違っていました。
カーネルがプログラムの開始コードに渡した引数と環境文字列は、他のプログラムデータと同様に通常のアプリケーション空間仮想メモリに格納され、他のプログラムデータ変数と同様に変更できます。プログラムがこれを修正することは完全に合法的です。
(これはカーネルの提供と強制の観点からのものです。特定のプログラミング言語の標準が必ずしも同じことを言うわけではありません。しかし、カーネルに関する限り、これは読み取り可能なアプリケーション空間領域です。プログラムデータのための書き込み可能な仮想メモリ、カーネルは機械語コードをコンパイルしたプログラミング言語には興味がありません。
この/proc/${PID}/environ
ファイルは、アプリケーション空間の仮想メモリのウィンドウに過ぎません。 Linuxはプロセスの実際の環境データを記憶せず、プロセスが開始された環境領域の開始アドレスと終了アドレスのみを記憶し、ファイルは/proc/${PID}/environ
現在メモリ内のすべてを読み込みます。このファイルに ␀ で終わる文字列のリストが含まれるとは思わないでください。これは間違った期待です。
これらの文字列を含むメモリを変更するGNU Cライブラリ関数はありません。ただし、さまざまなプログラムにはこれを行う独自の機能があります。
たとえば、OpenSSHを考えてみましょう。 OpenSSHサーバーは、ps
パラメータベクトルの表示内容を変更して読み込みますsshd: JdeBP [priv]
。
OpenSSHサーバーには、LinuxのOpenBSDでBSD Cライブラリの機能をエミュレートするコードが含まれています。 OpenBSDは、コマンドによって報告されたプロセス引数ベクトルをsetproctitle()
無視できるBSD Cライブラリ関数を呼び出します。ps
その呼び出しはsysctl()
カーネルに新しい引数ベクトルを渡し、これをps
使用して読み取ることができますsysctl()
。 FreeBSDにも同様の機能があります。
説明したように、Linuxでは、カーネルは実際のパラメータと環境を覚えておらず、プロセスを開始したときに元々配置されたメモリ領域の開始アドレスと終了アドレスのみを記憶します。したがって、OpenSSHのLinuxポートには、setproctitle()
上記のメモリ領域を上書きする互換性機能があります。
この互換性関数は、環境領域の合計サイズを計算します。そしてパラメータ領域とオーバーライドみんな新しいパラメータ文字列を使用してください。これは、通常の状況では、呼び出し元プログラムがsetproctitle()
プロセスが元々持っていたパラメータデータセットよりも長いパラメータデータを書き込もうとするためです。 sshd
これを頻繁にしなさい。したがって、新しいパラメータがパラメータ領域の後ろの環境領域を上書きできるようにして、より長いパラメータ文字列のセットを収容できるより多くのスペースをプログラムに提供します。
大事なことも未使用部分を埋めてくださいパラメータ全体と環境データの元の長さを␀sで上書きする必要はありません。
あなたが見るのはこれの正確な結果です。システムでOpenSSHサーバープロセスを見つけたら、それを見つけることができます/proc/${PID}/environ
。
追加読書
setproctitle
。 FreeBSD 11.0 マニュアル。setproctitle
。 OpenBSD マニュアル。setproctitle()
。 OpenSSHポータブルバージョン。environ_read()
。 fs/proc/base.fs/proc/base.fs/proc/base.fs/proc/base.fs/proc/base.fs/proc/base.fs Linuxカーネル。自由電子。
答え2
NUL
これは、環境変数があるメモリ位置にsを書き込むことで完全に可能です。
#include <stdio.h>
#include <unistd.h>
extern char **environ;
int main(void)
{
int i;
char *p = *environ;
/* hopefully your ENV is longer than this */
for (i = 0; i < 10; i++) *(p + i) = 0;
printf("hexdump -C /proc/%d/environ\n", getpid());
sleep(99999);
}
空の環境でプログラムを起動すると、ファイルはenviron
完全に空になります。
execle("/bin/sleep", "sleep", "999", (char *)NULL, (char *const) NULL)
したがって、この状況は、プロセスが実行された後にプロセスが実行する操作です(そして、メモリを何らかの方法でロックしないと、呼び出しにsetenv(3)
問題が発生する可能性がありますが、このような状況を防ぐ方法はほとんどありません...)。