JPCERT コーディネーションセンター

EXP01-J. オブジェクトが必要な場面で null を使わない

EXP01-J. オブジェクトが必要な場面で null を使わない

オブジェクトが必要とされるどの場面でも、null 値を使わない。これは以下の例を含む。

オブジェクトが必要な場面で null を使うと、NullPointerException がスローされ、プログラムやスレッドの実行が中断される。このコーディングスタンダードに準拠したコードは、「ERR08-J. NullPointerException およびその親クラスの例外をキャッチしない」にしたがって NullPointerException をキャッチしないため、結果として終了する。

違反コード

以下の違反コード例は Reasoning によって最初に発見された Tomcat version 4.1.24 のバグを示している[Reasoning 2003]。cardinality() メソッドは、col コレクションのなかに現れる obj オブジェクトの出現回数を返すように設計されたメソッドである。cardinality() メソッドの正しい使い道の一つは、コレクションのなかにあるオブジェクトのいくつが null であるのかを調べることである。しかし、コレクションに含まれるメンバをチェックするには、式 obj.equals(elt) を使うことになるので、objnull であり eltnull でない場合に null ポインタ参照が必ず発生する。

public static int cardinality(Object obj, final Collection<?> col) {
  int count = 0;
  if (col == null) {
    return count;
  }
  Iterator<?> it = col.iterator();
  while (it.hasNext()) {
    Object elt = it.next();
    if ((null == obj && null == elt) || obj.equals(elt)) {  // null ポインタ参照
      count++;
    }
  }
  return count;
}
適合コード

以下の適合コードでは、明示的なチェックを追加して null ポインタ参照を排除している。

public static int cardinality(Object obj, final Collection col) {
  int count = 0;
  if (col == null) {
    return count;
  }
  Iterator it = col.iterator();
  while (it.hasNext()) {
    Object elt = it.next();
    if ((null == obj && null == elt) ||
        (null != obj && obj.equals(elt))) {
      count++;
    }
  }
  return count;
}
違反コード

以下の違反コード例は、指定された String 引数が有効な名前(1つ以上のスペースで区切られた、大文字から始まる2つの単語)なら true を返す、isProperName() メソッドを定義している。

public boolean isProperName(String s) {
  String names[] = s.split(" ");
  if (names.length != 2) {
    return false;
  }
  return (isCapitalized(names[0]) && isCapitalized(names[1]));
}

isProperName() メソッドは、null を引数として呼び出されると null ポインタ参照を発生させるため、このルールに違反している。

適合コード(ラッパーメソッド)

以下の適合コードでは、isProperName() メソッドの実装は違反コードと同じだが、クラス内の1か所からしか呼び出されない private メソッドになっている。

public class Foo {
  private boolean isProperName(String s) {
    String names[] = s.split(" ");
    if (names.length != 2) {
      return false;
    }
    return (isCapitalized(names[0]) && isCapitalized(names[1]));
  }

  public boolean testString(String s) {
    if (s == null) return false;
    else return isProperName(s);
  }
}

isProperName() が有効な文字列参照についてのみ呼び出されることを、testString() メソッドが保証している。結果として、isProperName() メソッドが public だと違反になるにもかかわらず、このクラスはこのルールに適合している。このような保証を使うと、null ポインタ参照を排除できる。

適合コード(Optional 型)

以下の適合コードでは、null になりうる String オブジェクトの代わりに Optional String を使っている。Optional クラス(java.util.Optional [API 2014])は Java 8 で導入され、null ポインタ参照を軽減するために使える。

public boolean isProperName(Optional<String> os) {
  String names[] = os.orElse("").split(" ");
  return (names.length != 2) ? false :
         (isCapitalized(names[0]) && isCapitalized(names[1]));
}

Optional クラスのメソッドを使うと、プログラムを短く直感的にできる。[Urma 2014]

例外

EXP01-J-EX0: throws 節かメソッドコメントで NullPointerException をスローする(可能性がある)ことを文書化している場合、有効なオブジェクト参照であることの保証なしにオブジェクト型の引数を参照できる。ただし、この例外は控えめに使うべきである。

リスク評価

null ポインタ参照はサービス運用妨害(denial-of-service)につながる可能性がある。マルチスレッドプログラムにおいては、null ポインタ参照はキャッシュコヒーレンシポリシー(cache coherency policies)に違反し、リソースリークを引き起こす可能性がある。

ルール

深刻度

可能性

自動検出

自動修正

優先度

レベル

EXP01-J

不可

P6

L2

自動検出

null ポインタ参照はプログラムの実行フローに依存して発生する場合がある。ツールによる自動検出には限界があるため、人の手によるコード分析が必要となる場合もあるだろう[Hovemeyer 2007]。メソッド引数に(この引数の値は null ではない、というような意味の)アノテーションをつけることで、検出ツールの処理を助け、人の手によるコード分析の必要性を減らすことができる。このようなアノテーションを行うことは強く推奨される。

ツール バージョン チェッカー 説明
The Checker Framework 2.1.3

Nullness Checker
Initialization Checker
Map Key Checker

Null pointer errors (see Chapter 3)
Ensure all fields are set in the constructor (see Chapter 3.8)
Track which values are keys in a map (see Chapter 4)

CodeSonar

9.0p0

JAVA.DEEPNULL.PARAM.EACTUAL
JAVA.DEEPNULL.EFIELD
JAVA.DEEPNULL.FIELD
JAVA.NULL.PARAM.ACTUAL
JAVA.NULL.DEREF
JAVA.DEEPNULL.DEREF
JAVA.DEEPNULL.RET.EMETH
JAVA.DEEPNULL.RET.METH
JAVA.NULL.RET.ARRAY
JAVA.NULL.RET.BOOL
JAVA.NULL.RET.OPT
JAVA.STRUCT.UPD
JAVA.STRUCT.DUPD
JAVA.STRUCT.UPED
JAVA.DEEPNULL.PARAM.ACTUAL
Actual Parameter Element may be null
Field Element may be null (deep)
Field may be null (deep)
Null Parameter Dereference
Null Pointer Dereference
Null Pointer Dereference (deep)
Return Value may Contain null Element
Return Value may be null
Return null Array
Return null Boolean
Return null Optional
Unchecked Parameter Dereference
Unchecked Parameter Dereference (deep)
Unchecked Parameter Element Dereference (deep) 
null Passed to Method (deep)
Coverity

v7.5


FORWARD_NULL
NULL_RETURNS
REVERSE_INULL
FB.BC_NULL_INSTANCEOF
FB.NP_ALWAYS_NULL
FB.NP_ALWAYS_NULL_EXCEPTION
FB.NP_ARGUMENT_MIGHT_BE_NULL
FB.NP_BOOLEAN_RETURN_NULL
FB.NP_CLONE_COULD_RETURN_NULL
FB.NP_CLOSING_NULL
FB.NP_DEREFERENCE_OF_ READLINE_VALUE
FB.NP_DOES_NOT_HANDLE_NULL
FB.NP_EQUALS_SHOULD_HANDLE_ NULL_ARGUMENT
FB.NP_FIELD_NOT_INITIALIZED_ IN_CONSTRUCTOR
FB.NP_GUARANTEED_DEREF
FB.NP_GUARANTEED_DEREF_ON_ EXCEPTION_PATH
FB.NP_IMMEDIATE_DEREFERENCE_ OF_READLINE
FB.NP_LOAD_OF_KNOWN_NULL_ VALUE
FB.NP_NONNULL_FIELD_NOT_ INITIALIZED_IN_CONSTRUCTOR
FB.NP_NONNULL_PARAM_VIOLATION
FB.NP_NONNULL_RETURN_VIOLATION
FB.NP_NULL_INSTANCEOF
FB.NP_NULL_ON_SOME_PATH
FB.NP_NULL_ON_SOME_PATH_ EXCEPTION
FB.NP_NULL_ON_SOME_PATH_ FROM_RETURN_VALUE
FB.NP_NULL_ON_SOME_PATH_ MIGHT_BE_INFEASIBLE
FB.NP_NULL_PARAM_DEREF
FB.NP_NULL_PARAM_DEREF_ALL_ TARGETS_DANGEROUS
FB.NP_NULL_PARAM_DEREF_ NONVIRTUAL
FB.NP_PARAMETER_MUST_BE_NON - NULL_BUT_MARKED_AS_NULLABLE
FB.NP_STORE_INTO_NONNULL_FIELD
FB.NP_TOSTRING_COULD_ RETURN_NULL
FB.NP_UNWRITTEN_FIELD
FB.NP_UNWRITTEN_PUBLIC_OR_ PROTECTED_FIELD
FB.RCN_REDUNDANT_COMPARISON_ OF_NULL_AND_NONNULL_VALUE
FB.RCN_REDUNDANT_COMPARISON_ TWO_NULL_VALUES
FB.RCN_REDUNDANT_NULLCHECK_ OF_NONNULL_VALUE
FB.RCN_REDUNDANT_NULLCHECK_ OF_NULL_VALUE
FB.RCN_REDUNDANT_NULLCHECK_ WOULD_HAVE_BEEN_A_NPE

Implemented
Fortify V. 5.0

Missing_Check_against_Null
Null_Dereference
Redundant_Null_Check

Implemented
Findbugs V. 2.0

NP_DEREFERENCE_OF_READLINE_VALUE
NP_NULL_PARAM_DEREF
NP_TOSTRING_COULD_RETURN_NULL

Implemented
Klocwork

2025.2

NPE.COND
NPE.CONST
NPE.RET
NPE.RET.UTIL
NPE.STAT
REDUN.EQNULL

Parasoft Jtest 2024.2 CERT.EXP01.NP
CERT.EXP01.NCMD
Avoid NullPointerException
Ensure that dereferenced variables match variables which were previously checked for "null"
PVS-Studio

7.38

V6008V6073V6093
SonarQube 9.9

S2259
S2225
S2447
S2637

Null pointers should not be dereferenced
"toString()" and "clone()" methods should not return null
Null should not be returned from a "Boolean" method
"@NonNull" values should not be set to null
SpotBugs

4.6.0

NP_DEREFERENCE_OF_READLINE_VALUE
NP_IMMEDIATE_DEREFERENCE_OF_READLINE
NP_ALWAYS_NULL
NP_NULL_ON_SOME_PATH
NP_NULL_ON_SOME_PATH_EXCEPTION
NP_NULL_PARAM_DEREF
NP_NULL_PARAM_DEREF_NONVIRTUAL
NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS
NP_TOSTRING_COULD_RETURN_NULL
Implemented
関連する脆弱性

JDK 1.6 の update 4 より前のバージョンにおける Java Web Start アプリケーションとアプレットには、セキュリティ上重大な問題を引き起こすバグが存在した。場合によっては、アプリケーションやアプレットがサーバとの間で HTTPS 接続を確立しようとすることで NullPointerException が発生した[SDN 2008]。HTTPS によるセキュアな接続を確立できなかった結果、サービス運用妨害(denial-of-service)が発生した。クライアントはセキュアでない HTTP 接続による通信を一時的に行わざるを得なくなったのである。

関連ガイドライン

SEI CERT C コーディングスタンダード

EXP34-C. null ポインタを参照しない

ISO/IEC TR 24772:2010

Null Pointer Dereference [XYH]

MITRE CWE

CWE-476, NULL Pointer Dereference

実装の詳細 (Android)

Android アプリは、携帯端末のメモリが限られているため NullPointerException に対してより敏感である。static メンバや Activity のメンバは、メモリが不足すると null になる可能性がある。

参考文献

[API 2006]

Method doPrivileged()

[API 2014] Class java.util.Optional

[Hovemeyer 2007]


[Reasoning 2003]

"Defect ID 00-0001"
"Null Pointer Dereference"

[SDN 2008]

Bug ID 6514454

[ Seacord 2015 ]  Image result for video icon EXP01-J. Never dereference null pointers LiveLesson
[Urma 2014] Tired of Null Pointer Exceptions? Consider Using Java SE 8's Optional!
翻訳元

これは以下のページを翻訳したものです。

EXP01-J. Do not use a null in a case where an object is required (revision 167)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter