- numpy 1.16.3 以降
現象
Pythonコードの例
np.load('/path/to/file.npy')
発生するエラーの例
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-37-1db66562b57b> in <module>
----> 1 np.load('tmp.npy')
~/venv/aep/lib/python3.7/site-packages/numpy/lib/npyio.py in load(file, mmap_mode, allow_pickle, fix_imports, encoding)
451 else:
452 return format.read_array(fid, allow_pickle=allow_pickle,
--> 453 pickle_kwargs=pickle_kwargs)
454 else:
455 # Try a pickle
~/venv/aep/lib/python3.7/site-packages/numpy/lib/format.py in read_array(fp, allow_pickle, pickle_kwargs)
720 # The array contained Python objects. We need to unpickle the data.
721 if not allow_pickle:
--> 722 raise ValueError("Object arrays cannot be loaded when "
723 "allow_pickle=False")
724 if pickle_kwargs is None:
ValueError: Object arrays cannot be loaded when allow_pickle=False
原因
numpy v1.16.3
より、numpy.load()
関数の挙動が変更されたため。
変更前 | 変更後 |
---|---|
allow_pickle オプションの省略時のデフォルト値はTrue
|
allow_pickle オプションの省略時のデフォルト値はFalse
|
解決方法
後述するセキュリティ上の懸念がないことを確認した上で、以下のようにallow_pickle
オプションを指定してやれば良い。
np.load('/path/to/file.npy', allow_pickle=True)
解説
numpy行列とdtype
numpy行列(np.ndarray
)は数値だけでなく文字列やPythonオブジェクトを格納することができる。格納された値の種類は、dtype
という属性へと反映されている。
numpy v1.16.0
の脆弱性
Pythonオブジェクトが含まれたnumpy行列(をシリアライズしたファイル)をnp.load()
によってでシリアライズする際に、悪意のあるコードを実行できてしまうという脆弱性が報告されている。(ただしこの脆弱性に関しては反論がある)
そこでv1.16.3
より、np.load()
のデフォルトの挙動が前述したように変更され、dtype
がPythonオブジェクトである場合に、allow_pickle=False
ならばValueError
をスローするようになった。
より安全サイドへ倒すための仕様変更と言える。
セキュリティ上の懸念
当然のごとく、信頼できないファイルに対してnp.load(allow_pickle=True)
してはいけない。前節で述べたように、任意のコードを実行できてしまう可能性がある。
Jupyter等によるデータ整形や機械学習など、アドホックなコードなら普通は問題ない1。注意すべきはPythonを用いたアプリケーション開発者である。
これってBreaking Change じゃないの?
アプリケーションの挙動が変わってしまうので、当然Breaking Change(後方互換性のない変更)だと思う。
Pythonの数値計算系のライブラリには、デフォルト値の変更ならセーフみたいな風潮があるかもしれない。2リビジョンアップだから大丈夫でしょう、とか思っていたら痛い目に合う。
他の言語から参入したアプリケーションエンジニアはよく注意されたい。