イントロダクション
Redisはオープンソースのインメモリデータストアで、キャッシュ、メッセージブローカー、高性能NoSQLデータベースとして広く利用されています。文字列、ハッシュ、リスト、セット、ソート済みセット、ビットマップ、HyperLogLog、ストリームといった豊富なデータ構造をサポートし、アトミック操作と非常に低いレイテンシを特徴としています。RDBスナップショットとAOFによる永続化、レプリケーション、Sentinel、Clusterによる高可用性を実現しています。また、RedisはLuaによるサーバーサイドスクリプトをサポートしており、複雑な操作をアトミックに実行できます。そのスピード、柔軟性、そして成熟したエコシステムにより、Redisはレイテンシに敏感な現代のシステムの中核となる構成要素となっています。
2025年10月にRedisのバージョンがリリースされました 8.2.2組み込みLuaエンジンの4つの脆弱性を修正するセキュリティリリースです。最も深刻な脆弱性は、Luaの解放後使用(use-after-free)です。攻撃者はスクリプトサンドボックスを抜け出し、信頼できないユーザーが任意のLuaを実行できるホスト上でコードを実行できます。また、このリリースでは、unpackの整数オーバーフロー、クロスユーザースクリプト実行の脆弱性、Luaレキサーの境界外読み取りの脆弱性も修正されています。この記事では、各CVEについて詳しく説明します。 パッチを歩く、Redis の導入を強化するための実践的な手順を説明します。
脆弱性の概要
コードに進む前に、8.2.2 で Lua エンジンに修正された内容の簡単なマップを示します。
CVE-2025-46817: 解凍時の整数オーバーフロー (CVSS 9.8、重大)
unpack(tbl, i, j) に極端な引数を渡すと、返される値の内部カウントがオーバーフローする可能性があります。このカウントの誤りはスタックチェックを回避し、クラッシュやメモリ破損を引き起こす可能性があるため、エクスプロイトに利用されるプリミティブです。
CVE-2025-46818: 共有ランタイム状態を介したクロスユーザースクリプト実行 (CVSS 7.3、高)
メタテーブルと環境関連APIに関する緩い制御により、あるスクリプトが他のスクリプトの実行に影響を与える可能性がありました。一部の設定では、権限の低いユーザーが別のユーザーのコンテキストでの実行に影響を与える可能性があり、分離性が弱まる可能性がありました。
CVE-2025-46819: Lua の長い文字列解析における境界外読み取り (CVSS 7.1、高)
Luaの字句解析器における長い文字列/長いコメント区切り文字([=[ … ]=]など)の不安定な処理により、バッファの終端を越えてしまう可能性があります。その結果、Redisがクラッシュし、場合によっては近傍のメモリが露出する可能性があります。
CVE-2025-49844: パーサーにおける Lua の解放後使用 (CVSS 10.0、重大)
細工された Lua スクリプトは、組み込みの Lua エンジンで use-after-free にヒットするような方法でガベージ コレクションと解析を操作する可能性があります。
次に、関連するパッチスニペットを使って、それぞれの詳細を見ていきます。
修正の内幕:脆弱性の徹底分析
CVE-2025-46817: アンパックにおける整数オーバーフロー
この問題は、Redisに組み込まれているLua 5.1のunpack(tbl, i, j)に影響します。i/jの値が極端に高い場合、「何個の結果を返すか」という計算でオーバーフローが発生し、誤ったカウントが生成され、スタックチェックをすり抜ける可能性があります。これはクラッシュやメモリ破損につながる可能性があり、悪意のあるシナリオではリモートコード実行(RCE)の構成要素として悪用される可能性があります。
この CVE のパッチは、次の場所で参照できます。 fc9abc7
パッチ分析
この修正により、unpack の範囲処理が明示的かつ安全になります。
- カウント計算を安全な符号なし演算に切り替えました
パッチ前:
静的int luaB_unpack(lua_State *L) {
整数 i、e、n;
luaL_checktype(L, 1, LUA_TTABLE);
i = luaL_optint(L, 2, 1);
e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1));
if (i > e) return 0; /* 空の範囲 */
n = e – i + 1; /* オーバーフローする可能性があります (符号付き) */
n <= 0 || !lua_checkstack(L, n) の場合
luaL_error(L, “展開するには結果が多すぎます”); を返します。
/* n 個の結果をプッシュします… */
}
ここで、e – i + 1 は signed int で実行されます。極端な値(例:0 と 2147483647)ではオーバーフローが発生する可能性があり、n <= 0 は信頼できるガードではありません。
パッチ後:
静的int luaB_unpack(lua_State *L) {
整数 i, e;
符号なし整数 n;
luaL_checktype(L, 1, LUA_TTABLE);
i = luaL_optint(L, 2, 1);
e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1));
if (i > e) return 0; /* 空の範囲 */
n = (unsigned int)e – (unsigned int)i; /* 要素数から1を引いた値 */
if (n >= INT_MAX || !lua_checkstack(L, ++n))
luaL_error(L, “展開するには結果が多すぎます”); を返します。
lua_rawgeti(L, 1, i); /* arg[i]をプッシュする */
(i++ < e) の間 {
lua_rawgeti(L, 1, i);
}
n を返します。
}
キーポイント:
- 負の値やオーバーフローを回避するために、スパンに unsigned int を使用します。
- n を「count - 1」として扱い、スタックチェックの前に 1 増加します。
- 強制する 厳格な上限 (n >= INT_MAX → エラー)。
- スタックにすべての結果のためのスペースがあることを確認する 何でも押します。
- 配列のインデックスを明示的にする
表コード内の関連する締め付け:
パッチ後
if (1 <= キー && キー <= t->サイズ配列)
&t->array[キー-1]を返します。
符号なしキャスト トリックに頼る代わりに、明確な境界チェックを使用することで、巨大なインデックスや負のインデックスによる異常な動作を回避します。
これらの変更により、極端なアンパック範囲によってカウントのラップや安全でないプッシュが発生しなくなりました。サイズが大きすぎる呼び出しは、クラッシュやメモリ破損のリスクを負うことなく、「結果が多すぎてアンパックできません」という明確なエラーで失敗するようになりました。
CVE-2025-46819: Lua の長い文字列解析における境界外読み取り
この問題はLuaの字句解析器、具体的には長い文字列や[=[ … ]=]のような長いコメントの解析方法に起因します。脆弱なバージョンでは、特定の不正な、あるいは極端な区切り文字パターンによって、字句解析器が入力バッファの終端を超えて解析を行う可能性があります。その結果、Redisがクラッシュ(DoS攻撃)する可能性があり、場合によっては近傍メモリのバイトが露出する可能性もあります。
この CVE のパッチは、次の場所で参照できます。 3a1624d
パッチ分析
この修正により、長い文字列の処理がより厳密になり、脆弱性が軽減されます。
区切り文字の解析が明確になります
[===[ や ]===] のようなシーケンスを解析するヘルパーは、より安全な型を使用して明確で一貫性のある値を返すように更新されました。
パッチ後
静的size_t skip_sep(LexState *ls) {
size_t カウント = 0;
int s = ls->current; /* '[' または ']' */
lua_assert(s == '[' || s == ']');
save_and_next(ls); /* 消費する */
while (ls->current == '=') { /* count '=' */
保存して次へ(ls);
count ++;
}
if (ls->current == s) /* 2番目の '[' または ']' に一致 */
return count + 2; /* 有効なlong区切り文字 */
そうでない場合 (count == 0)
1を返す; /* 長い文字列/コメントではない */
ほかに
0を返す; /* 不正な形式 */
}
- 有効な長い文字列区切り文字は、明確に定義された長さ (>= 2) を返すようになりました。
- 平野[きれいにフォールバックします(1)。
- 不正な [==… パターンは 0 を返し、エラーとして扱われます。
これにより、奇妙または不完全なパターンによってレキサーが混乱し、バッファを超えて読み取られる可能性があるギャップが解消されます。
ボディスライスのオフセットを修正
実際の文字列コンテンツを抽出するときに、パッチはスライスの計算を skip_sep の結果に合わせて調整します。
パッチ適用後:
if (seminfo) {
seminfo->ts = luaX_newstring(
ls、
luaZ_buffer(ls->buff) + sep,
luaZ_bufflen(ls->buff) – 2 * 9 月
);
}
以前は、オフセットが区切り文字のセマンティクスと適切に一致していなかったため、微妙な off-by-N の動作が発生する可能性がありました。現在は、開始位置と長さが sep から直接取得されるため、字句解析は境界内に収まります。
型はコードの実際の動作と一致する
このロジックに関係するカウンターとインデックスでは、オーバーロードされた signed int ではなく適切な unsigned/size 型が使用されるようになり、負の値やラップアラウンドがポインター演算に侵入するリスクが軽減されます。
これにより、巨大な、あるいは意図的に改ざんされた [=…=[ … ]=…=] シーケンスであっても、Lua は正しく解析するか、通常のエラーとして拒否するようになりました。悪意のあるスクリプトがレキサーを誘導してバッファの末尾を超えて解析させるような経路はなくなりました。
CVE-2025-46818: Lua スクリプトが別のユーザーのコンテキストで実行される可能性がある
この問題は Lua スクリプトのスコープを、それが属するユーザーと環境に限定する脆弱なバージョンでは、コアタイプのメタテーブルやレガシー環境APIといった共有ランタイム要素が緩く、1つのスクリプトが他のスクリプトの実行に影響を与える可能性がありました。一部の環境では、ユーザー間の分離が曖昧になる可能性がありました。
この CVE のパッチは、次の場所で参照できます。 45eac02
パッチ分析
この修正は主に 2 つのことを行います。 コアメタテーブルを保護する の三脚と 危険なレガシーAPIを遮断.
- コア型メタテーブルは保護されている
以前は、スクリプトに公開されるコアLua型(文字列、数値、ブール値など)には、ユーザースクリプトによるメタテーブルの変更を厳格に防止する障壁がありませんでした。つまり、共有メタテーブルの変更は、同じエンジンで実行される他のコードに影響を与える可能性がありました。
パッチを当てたコードは、Lua環境のセットアップ時にこれらのメタテーブルを明示的に強化します。簡略化すると以下のようになります。
修正されたコード: プリミティブメタテーブルを保護対象としてマークする
静的 void luaProtectPrimitiveMetatables(lua_State *L) {
const int タイプ[] = {LUA_TSTRING、LUA_TNUMBER、LUA_TBOOLEAN、LUA_TNIL};
(size_t i = 0; i < sizeof(types)/sizeof(types[0]); i++) {
luaL_getmetatable(L, lua_typename(L, types[i]));
もし(!lua_isnil(L, -1)) {
lua_pushliteral(L, “保護されています”);
lua_setfield(L, -2, “__metatable”); /* メタテーブルをロックする */
}
lua_pop(L, 1);
}
}
この__metatable保護により、ユーザースクリプトは これらの型は、メタテーブルをグローバルに置き換えたり書き換えたりすることはできません。メタテーブルをグローバルに置き換えたり書き換えたりしようとすると、他のすべての動作を黙って変更するのではなく、きれいに失敗します。
- レガシー環境APIは明示的にオプトインされている
環境を操作できる古いLua APIは、 デフォルトでは無効 オペレーターが明示的に有効にした場合にのみ公開されます。
概念的には、変更は次のようになります。
パッチ前: 非推奨の API はスクリプト環境で常に利用可能
静的const luaL_Reg redis_compat_funcs[] = {
{“getfenv”、luaB_getfenv}、
{“setfenv”、luaB_setfenv}、
{“newproxy”, luaB_newproxy},
{NULL、NULL}
};
スクリプト作成環境(…) {
luaL_register(L, “_G”, redis_compat_funcs);
}
パッチ適用後: lua-enable-deprecated-api が設定されている場合にのみ登録します
スクリプト作成環境(…) {
if (server.lua_enable_deprecated_api) {
luaL_register(L, “_G”, redis_compat_funcs);
}
}
lua-enable-deprecated-api が有効になっていない場合、これらの関数はスクリプト環境に存在しないため、クロスコンテキスト操作のための強力な一連の手段が削除されます。
これらの変更をまとめると、次のことが保証されます。
- スクリプトは、コアメタテーブルを書き換えることで、他のすべてのスクリプトの基本的な動作を黙って修正することはできません。
- 環境操作プリミティブは の オペレーターが意図的にオンにした場合に利用可能になります。
これにより、Lua ランタイムの予測可能性が高まり、各スクリプトが意図された権限とコンテキストにさらに近くなります。これは、共有またはマルチテナントの Redis 展開にとって重要です。
CVE-2025-49844: Lua パーサーにおける解放後使用
このバグは、Redis が Lua パーサーを統合する方法に存在します。特定の条件下では、細工された Lua スクリプトによって不適切なタイミングでガベージコレクションがトリガーされ、Redis が既に解放されているメモリへのポインタを読み取ってしまう可能性があります。
この CVE のパッチは、次の場所で参照できます。 d5728cb5795
パッチ分析
重要な変更点は、Redis が解析中にパーサーが使用している Lua オブジェクト (チャンク名など) への適切な参照を Lua スタック上に保持するようになったため、ガベージ コレクターはそれらを生きているものとして認識し、早期に解放したり移動したりすることができなくなったことです。

以前は、チャンク「name」はインラインで作成され、直接レキサーに渡されていました。
luaX_setinput(L, &lexstate, z, luaS_new(L, 名前));
その値は明確に固定されていなかったため (たとえば、Lua スタック上)、GC またはデフラグの不運なインターリーブと積極的なスクリプトにより、理論上は、移動または解放されたものへのポインターをパーサーが保持したままになる可能性があります。
パッチを当てたコードでは、所有権が明示的に示されます。
TString *tname = luaS_new(L, 名前);
setsvalue2s(L, L->top, tname); /* 名前をスタック上に保持する */
incr_top(L);
luaX_setinput(L, &lexstate, z, tname); /* 字句解析/構文解析に安全に使用します */
/* … チャンクを解析します … */
–L->top; /* 完了したら参照を削除します */
この状態をパース全体にわたって参照し続けることで、GCとデフラグはそれをライブとして扱い、実行中に再利用したり再配置したりすることができなくなります。これにより、解放後使用を可能にしていた狭いタイミングウィンドウが実質的に排除されます。
リスクのある展開シナリオ
これらの問題は、次のような環境で特に当てはまります。
- アプリケーションは Redis で Lua スクリプトを使用します。
- 同じ Redis クラスターがチームまたはテナント間で共有されます。
- Redis には、信頼できない入力やユーザーが制御する入力から直接またはアプリケーション経由でアクセスすることができます。
推奨されるアクション
1. まずパッチを当てる
- パッチを適用した Redis 8.2.2 ビルドにアップグレードします。
- 信頼されていないコードや半信頼のコードが Lua を実行できる場合 (たとえば、EVAL / SCRIPT LOAD 経由)、または Redis クラスターがチーム/テナント間で共有される場合は、これを高優先度として扱います。
2. Luaスクリプトをロックダウンする
- ACL を使用して、Lua スクリプトを信頼できるサービスのみに制限します。
- Redis をプライベート ネットワーク上に保持し、インターネットに直接公開しないでください。
- 明確で監査された必要性がない限り、lua-enable-deprecated-api を無効のままにしておきます。
3. 機密性の高いワークロードをセグメント化する
- 信頼されていない Lua または顧客主導の Lua を別の Redis インスタンスで実行します。
- テナント制御のスクリプトを、重要なデータや機密データを保持するクラスターと混在させないでください。
4. セキュリティ制御を使用して可視性を高める(パッチ適用の代替としてではない)
- IPS/IDS/WAF を構成して、Redis への直接アクセスと疑わしい Redis プロトコルの使用を検出してブロックします。
- Redis ホストで EDR/AV/ランタイム保護を使用して、エクスプロイトのような動作を検出します。
参照:
- https://github.com/redis/redis/commits/8.2.2/
- https://nvd.nist.gov/vuln/detail/CVE-2025-46817
- https://nvd.nist.gov/vuln/detail/CVE-2025-46818
- https://nvd.nist.gov/vuln/detail/CVE-2025-46819
- https://nvd.nist.gov/vuln/detail/CVE-2025-49844
著者:
ヴィネイクマール
アドリプ・ムケルジー
ナンディニ・セス
スワンジート・ジャグタップ


