バッファ範囲チェック
バッファ範囲チェック
このレッスンで分かること
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 <= bufferSizeJavaScript
function isSafeWrite(bufferSize, offset, length) {
if (offset < 0 || length < 0) return false;
return offset + length <= bufferSize;
}負の値を許さない
offset = -1 や length = -1 のような 負の値 も アンダーフロー として false を返す仕様にします。length = 0 は 何も書かない 状態なので、offset が 0 〜 bufferSize の範囲なら true を返します。
== か <= か
初心者がよく間違えるのが offset + length < bufferSize と書くケース。これだと ちょうど末尾まで使う 安全な書き込みも false になります。配列 の index は 0 から size - 1 ですが、書き込み長さ は size までフル で OK なので、判定式は必ず <= です。
1 違いのバグをoff-by-one errorと呼びます。境界を扱う関数は等しいケースを必ずテストするのが鉄則です。
実務での応用例
Node.jsのBuffer.write(string, offset, length)でこのチェックを自分で行う必要がある場面がある- 通信プロトコルの
パケットパーサで、ヘッダのlengthフィールドが本文サイズを超えないかチェックする - ファイル書き込み API で、
シーク位置 + 書き込みサイズがファイルの最大サイズを超えないか
防御的プログラミングの定石
本問題のような 事前検証 は assert 系で組み込むのも一つの手です。Java の Objects.checkFromIndexSize(fromIndex, size, length) は同じ判定を標準ライブラリで提供しています。本問の変数との対応は fromIndex=offset、size=length(書き込み長)、length=bufferSize(バッファ全長)です。C++ 20 の std::span も 境界チェック付きのビュー を提供します。標準で用意されているチェックがあるなら、それを使うのが第一選択肢です。
よくある間違い
- 比較が
<で境界ぴったりのケースを弾いてしまう - 負の値のチェックを忘れて、
-1をbufferSize未満として通してしまう offset > bufferSizeのときにlength = 0でtrueを返してしまう
やってみよう
- 「収まらないとき何バイト余分か」を返す関数
overflowBytesを書く - 整数オーバーフローを考慮した版を書く (
offset + lengthがint 上限を超えるケース) - 複数の書き込みリクエストをまとめて検証する関数
validateBatchを書いてみる - 安全な書き込み位置を
next free offsetとして返す関数を書く
補足: 歴史と整数オーバーフロー
C の gets strcpy のように境界チェックを行わない関数の誤用は、過去に Morris ワーム (1988) や Heartbleed (2014) など大規模な脆弱性として現れてきました。詳細は セキュリティ 領域の話なので深入りしませんが、根っこは本問と同じ「offset + length が bufferSize を超えていないか」です。なお Java では offset + length が int の上限を超えると 2 の補数でラップして 負の値 になります。C の符号付き整数オーバーフローは規格上 未定義動作 であり何が起きるかは保証されません。Java 側は Math.addExact で例外として検出できます。
よくある質問
Q. このトピックを覚える意味は何ですか?
A. バッファオーバーフローは低レベル言語(C/C++等)では深刻なセキュリティ脆弱性に直結する古典的バグです。境界条件(<=か<か)を1文字間違えるだけで、off-by-oneエラーとして本番障害や脆弱性の温床になります。判定ロジックを自分の手で書けるようになっておくことが重要です。
Q. 実務でこの知識を使う場面は?
A. ファイルアップロードのサイズ検証、固定長バッファへの書き込み処理、ネットワークパケットのパース処理など、外部からの入力を固定サイズの領域に書き込む場面全般で登場します。Python/JavaScriptのような境界チェック付き言語でも、スライスやTypedArrayを扱うときは同じ罠があります。
Q. 他の章とどう繋がりますか?
A. 前のレッスンのエンディアン入れ替えと同様、メモリをバイト単位の領域として扱う視点の続きです。この章で学んだ境界チェックの考え方は、次章以降のデータ構造(配列・スタック・キュー)のインデックス操作でも繰り返し使うことになります。
次のレッスン
次は 第5章まとめクイズ に進みましょう。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- 範囲チェック の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. 範囲チェック とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- isSafeWrite という名前の関数を実装すること
- offset と length が 0 以上であることを確認すること
- 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