バッファ範囲チェック

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

バッファ範囲チェック

このレッスンで分かること

  • 1 違い のバグを off-by-one error と呼びます
  • バッファオーバーフロー は、配列メモリ領域範囲外 に書き込んでしまう古典的バグです
  • バッファのサイズ bufferSize と、書き込みたい先頭 offset と長さ length を受け取り、書き込みが範囲内に収まるか を返す関数を作ります

バッファ範囲チェック とは

オフセットと長さが固定サイズバッファに収まるかを判定する。本レッスンでは、バッファ範囲チェック の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。

バッファオーバーフロー は、配列メモリ領域範囲外 に書き込んでしまう古典的バグです。固定長のバッファに収まらないデータを書き込むと、隣接するメモリを壊してしまう のがその核心です。Python JavaScript のように 配列の境界 が自動チェックされる言語でも、スライスBuffer を扱うときには同じ罠が顔を出します。

配列の長さ を意識するのは、型システム がそれをやってくれない場面で特に重要です。コードレビュー で必ず指摘されるポイントの一つ。

この問題のルール

バッファのサイズ bufferSize と、書き込みたい先頭 offset と長さ length を受け取り、書き込みが範囲内に収まるか を返す関数を作ります。true なら OK、false ならオーバーフローです。

判定式はシンプルに次のようになります。

プレーンテキスト

offset >= 0 かつ length >= 0 かつ offset + length <= bufferSize

ここで <= が正しく、< ではないことが大事です。bufferSize = 10 offset = 0 length = 10 のとき、index 0 から 9 までを使うので ちょうど収まる ケースで true を返さなければなりません。

Python

def isSafeWrite(bufferSize, offset, length): if offset < 0 or length < 0: return False return offset + length <= bufferSize

JavaScript

function isSafeWrite(bufferSize, offset, length) { if (offset < 0 || length < 0) return false; return offset + length <= bufferSize; }

負の値を許さない

offset = -1length = -1 のような 負の値アンダーフロー として false を返す仕様にします。length = 0何も書かない 状態なので、offset0 〜 bufferSize の範囲なら true を返します。

diagram (will load when visible)

==<=

初心者がよく間違えるのが offset + length < bufferSize と書くケース。これだと ちょうど末尾まで使う 安全な書き込みも false になります。配列index0 から size - 1 ですが、書き込み長さsize までフル で OK なので、判定式は必ず <= です。

1 違い のバグを off-by-one error と呼びます。境界 を扱う関数は 等しいケース を必ずテストするのが鉄則です。

実務での応用例

  • Node.jsBuffer.write(string, offset, length) でこのチェックを自分で行う必要がある場面がある
  • 通信プロトコルの パケットパーサ で、ヘッダの length フィールドが 本文サイズ を超えないかチェックする
  • ファイル書き込み API で、シーク位置 + 書き込みサイズ がファイルの最大サイズを超えないか

防御的プログラミングの定石

本問題のような 事前検証assert 系で組み込むのも一つの手です。JavaObjects.checkFromIndexSize(fromIndex, size, length) は同じ判定を標準ライブラリで提供しています。本問の変数との対応は fromIndex=offsetsize=length(書き込み長)、length=bufferSize(バッファ全長)です。C++ 20std::span境界チェック付きのビュー を提供します。標準で用意されているチェックがあるなら、それを使うのが第一選択肢です。

よくある間違い

  • 比較が <境界ぴったり のケースを弾いてしまう
  • 負の値のチェックを忘れて、-1bufferSize 未満として通してしまう
  • offset > bufferSize のときに length = 0true を返してしまう

やってみよう

  • 「収まらないとき何バイト余分か」を返す関数 overflowBytes を書く
  • 整数オーバーフローを考慮した版を書く (offset + lengthint 上限 を超えるケース)
  • 複数の書き込みリクエストをまとめて検証する関数 validateBatch を書いてみる
  • 安全な書き込み位置を next free offset として返す関数を書く

補足: 歴史と整数オーバーフロー

Cgets strcpy のように境界チェックを行わない関数の誤用は、過去に Morris ワーム (1988) や Heartbleed (2014) など大規模な脆弱性として現れてきました。詳細は セキュリティ 領域の話なので深入りしませんが、根っこは本問と同じ「offset + lengthbufferSize を超えていないか」です。なお Java では offset + lengthint の上限を超えると 2 の補数でラップして 負の値 になります。C の符号付き整数オーバーフローは規格上 未定義動作 であり何が起きるかは保証されません。Java 側は Math.addExact で例外として検出できます。

よくある質問

Q. このトピックを覚える意味は何ですか?

A. バッファオーバーフローは低レベル言語(C/C++等)では深刻なセキュリティ脆弱性に直結する古典的バグです。境界条件(<=<か)を1文字間違えるだけで、off-by-oneエラーとして本番障害や脆弱性の温床になります。判定ロジックを自分の手で書けるようになっておくことが重要です。

Q. 実務でこの知識を使う場面は?

A. ファイルアップロードのサイズ検証、固定長バッファへの書き込み処理、ネットワークパケットのパース処理など、外部からの入力を固定サイズの領域に書き込む場面全般で登場します。Python/JavaScriptのような境界チェック付き言語でも、スライスやTypedArrayを扱うときは同じ罠があります。

Q. 他の章とどう繋がりますか?

A. 前のレッスンのエンディアン入れ替えと同様、メモリをバイト単位の領域として扱う視点の続きです。この章で学んだ境界チェックの考え方は、次章以降のデータ構造(配列・スタック・キュー)のインデックス操作でも繰り返し使うことになります。

次のレッスン

次は 第5章まとめクイズ に進みましょう。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. 範囲チェック の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. 範囲チェック とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. isSafeWrite という名前の関数を実装すること
  2. offset と length が 0 以上であることを確認すること
  3. offset + length <= bufferSize なら true を返すこと

入出力例

test-cases.txt

isSafeWrite(10, 0, 10)true isSafeWrite(10, 5, 5)true isSafeWrite(10, 5, 6)false isSafeWrite(10, -1, 5)false isSafeWrite(10, 0, 0)true isSafeWrite(10, 0, -1)false isSafeWrite(10, 10, 0)true

ヒント

main.py
main.py
学習モード

メモ

バッファ範囲チェック

⌘S で保存