classification
Title: PosixPath() constructor should not accept strings with embedded NUL bytes
Type: behavior Stage: needs patch
Components: Library (Lib) Versions: Python 3.4, Python 3.5
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: ischwabacher, pitrou, vajrasky
Priority: normal Keywords: patch

Created on 2014-08-05 19:44 by ischwabacher, last changed 2015-02-13 01:22 by demian.brecht.

Files
File name Uploaded Description Edit
embedded_null_in_path.patch vajrasky, 2014-08-09 07:53 review
Messages (4)
msg224880 - (view) Author: Isaac Schwabacher (ischwabacher) * Date: 2014-08-05 19:44
This is listed as a python3.4 issue even though I only tried this on the python2.7 backport because I don't have a python3 handy, but I was not able to find an indication, either here or elsewhere, that this had been addressed.  Please forgive me if it has.

The `pathlib.PosixPath()` constructor currently accepts strings containing NUL bytes, converting them into paths containing NUL bytes. POSIX specifies that a pathname may not contain embedded NULs.

It appears that `PosixPath.stat()` is checking for embedded NUL, but `PosixPath.open()` is not!  For safety, constructing a `PosixPath` with embedded NULs should be forbidden.

`pathlib.WindowsPath()` should probably receive the same treatment.

Observed behavior:

```python

>>> from pathlib import Path

>>> Path("\0I'm not malicious, I'm mischievous!")
PosixPath("\x00I'm not malicious, I'm mischievous!")

>>> _.open()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File ".../site-packages/pathlib.py", line 1077, in open
 return io.open(str(self), mode, buffering, encoding, errors, newline)
IOError: [Errno 2] No such file or directory: ''

>>> Path('/') / _
PosixPath("/\x00I'm not malicious, I'm mischievous!")

>>> _.open()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File ".../site-packages/pathlib.py", line 1077, in open
 return io.open(str(self), mode, buffering, encoding, errors, newline)
IOError: [Errno 21] Is a directory: "/\x00I'm not malicious, I'm mischievous!"

>>> _.stat()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File ".../site-packages/pathlib.py", line 1051, in stat
 return self._accessor.stat(self)
 File ".../site-packages/pathlib.py", line 346, in wrapped
 return strfunc(str(pathobj), *args)
TypeError: must be encoded string without NULL bytes, not str

>>> p1 = Path('/etc/passwd\0/hello.txt').open()

>>> p2 = Path('/etc/passwd').open()

>>> os.path.sameopenfile(p1.fileno(), p2.fileno())
True  # DANGER WILL ROBINSON!

```

Expected behavior:

```python

>>> Path("/\0I'm not malicious, I'm mischievous!")
...
ValueError: Illegal byte '\x00' in path

```
msg224882 - (view) Author: Isaac Schwabacher (ischwabacher) * Date: 2014-08-05 20:17
Further digging reveals that the issue with `open()` was fixed in #13848 (the bug was in the `io` module).  I still believe that this should fail in the `pathlib.Path` constructor, but this is less of a security issue.
msg225095 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2014-08-09 03:16
This sounds like a reasonable request indeed.
msg225102 - (view) Author: Vajrasky Kok (vajrasky) * Date: 2014-08-09 07:53
Here is the patch.
History
Date User Action Args
2015-02-13 01:22:24demian.brechtsetnosy: - demian.brecht
2014-08-09 07:53:43vajraskysetfiles: + embedded_null_in_path.patch

nosy: + vajrasky
messages: + msg225102

keywords: + patch
2014-08-09 03:16:57pitrousetstage: needs patch
2014-08-09 03:16:51pitrousetmessages: + msg225095
components: + Library (Lib)
versions: + Python 3.5
2014-08-08 19:36:11terry.reedysetnosy: + pitrou
2014-08-07 04:30:50demian.brechtsetnosy: + demian.brecht
2014-08-05 20:17:17ischwabachersettype: security -> behavior
messages: + msg224882
2014-08-05 19:44:08ischwabachercreate