Skip to content

gh-137335: Fix unlikely name conflicts for named pipes in multiprocessing and asyncio on Windows#137389

Merged
serhiy-storchaka merged 9 commits intopython:mainfrom
serhiy-storchaka:temp-named-pipes
Feb 24, 2026
Merged

gh-137335: Fix unlikely name conflicts for named pipes in multiprocessing and asyncio on Windows#137389
serhiy-storchaka merged 9 commits intopython:mainfrom
serhiy-storchaka:temp-named-pipes

Conversation

@serhiy-storchaka
Copy link
Member

@serhiy-storchaka serhiy-storchaka commented Aug 4, 2025

Since os.stat() raises an OSError for existing named pipe "\\.\pipe\...", os.path.exists() always returns False for it, and tempfile.mktemp() can return a name that matches an existing named pipe.

So, tempfile.mktemp() cannot be used to generate unique names for named pipes. Instead, CreateNamedPipe() should be called in a loop with different names until it completes successfully.

…processing and asyncio on Windows

Since os.stat() raises an OSError for existing named pipe "\\.\pipe\...",
os.path.exists() always returns False for it, and tempfile.mktemp() can
return a name that matches an existing named pipe.

So, tempfile.mktemp() cannot be used to generate unique names for named
pipes. Instead, CreateNamedPipe() should be called in a loop with
different names until it completes successfully.
@serhiy-storchaka serhiy-storchaka added needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes labels Aug 4, 2025
@serhiy-storchaka serhiy-storchaka changed the title gh-137335: Fix unlikely nmae conflicts for named pipes in multiprocessing and asyncio on Windows gh-137335: Fix unlikely name conflicts for named pipes in multiprocessing and asyncio on Windows Aug 4, 2025
address, openmode, _winapi.PIPE_WAIT,
1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL)
while True:
address = r'\\.\pipe\python-pipe-' + random.randbytes(8).hex()
Copy link
Member

@picnixz picnixz Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment:

we could use os.urandom(8).hex() but this would only reduce the number of loop iterations we would do until we find a suitable name. OTOH, this makes normal cases much slower (usually, we are not in the presence of an adversary that is trying to create pipes...), so we would be only doing the loop once.

Now, I'm actually worried that it if we're able to interact with the process that is creating the pipes, then we could actually recover enough samples from the underlying PRNG instance and get the original seed. But this is only if 1) there are no random calls in between and 2) we can get 624 consecutive 32-bit samples from the random source.

Condition 2) already holds because random.randbytes(8) is actually equivalent to two calls of random.randbytes(4) that are concatenated. Since reverting MT-19937 requires only consecutive 624 32-bit words, this is the same as doing 312 pipe creations where we don't have calls to random.* in between (and then inspect the named file). This could be possible in practice, especially if we're talking about the multiprocessing and the asyncio components which could have some interactivness (e.g., a server).

Therefore, I would still suggest using os.urandom(8).hex() even if it slows down the calls.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, I did not thought about this.

Sorry for not answering immediately, I missed your comment.

@serhiy-storchaka serhiy-storchaka requested a review from a team February 5, 2026 18:59
Copy link
Contributor

@kumaraditya303 kumaraditya303 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asyncio changes LGTM

@zooba
Copy link
Member

zooba commented Feb 9, 2026

LGTM too. I'll be happy to see the mktemp calls gone.

break
except OSError as e:
if e.winerror not in (_winapi.ERROR_PIPE_BUSY,
_winapi.ERROR_ACCESS_DENIED):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an infinite loop retrying on this error could potentially hide an error if the system simply doesn't allow this process to create a pipe with the given address? (here and below)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not know if this is even possible (\\.\pipe\ is not a real directory which you can forbid access to), but it will not harm if limit the number of attempts.

@serhiy-storchaka serhiy-storchaka merged commit d6a71f4 into python:main Feb 24, 2026
46 of 47 checks passed
@miss-islington-app
Copy link

Thanks @serhiy-storchaka for the PR 🌮🎉.. I'm working now to backport this PR to: 3.13, 3.14.
🐍🍒⛏🤖

@serhiy-storchaka serhiy-storchaka deleted the temp-named-pipes branch February 24, 2026 11:27
miss-islington pushed a commit to miss-islington/cpython that referenced this pull request Feb 24, 2026
…processing and asyncio on Windows (pythonGH-137389)

Since os.stat() raises an OSError for existing named pipe "\\.\pipe\...",
os.path.exists() always returns False for it, and tempfile.mktemp() can
return a name that matches an existing named pipe.

So, tempfile.mktemp() cannot be used to generate unique names for named
pipes. Instead, CreateNamedPipe() should be called in a loop with
different names until it completes successfully.
(cherry picked from commit d6a71f4)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
miss-islington pushed a commit to miss-islington/cpython that referenced this pull request Feb 24, 2026
…processing and asyncio on Windows (pythonGH-137389)

Since os.stat() raises an OSError for existing named pipe "\\.\pipe\...",
os.path.exists() always returns False for it, and tempfile.mktemp() can
return a name that matches an existing named pipe.

So, tempfile.mktemp() cannot be used to generate unique names for named
pipes. Instead, CreateNamedPipe() should be called in a loop with
different names until it completes successfully.
(cherry picked from commit d6a71f4)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
@bedevere-app
Copy link

bedevere-app bot commented Feb 24, 2026

GH-145170 is a backport of this pull request to the 3.14 branch.

@bedevere-app bedevere-app bot removed the needs backport to 3.14 bugs and security fixes label Feb 24, 2026
@bedevere-app
Copy link

bedevere-app bot commented Feb 24, 2026

GH-145171 is a backport of this pull request to the 3.13 branch.

@bedevere-app bedevere-app bot removed the needs backport to 3.13 bugs and security fixes label Feb 24, 2026
serhiy-storchaka added a commit that referenced this pull request Feb 24, 2026
…iprocessing and asyncio on Windows (GH-137389) (GH-145171)

Since os.stat() raises an OSError for existing named pipe "\\.\pipe\...",
os.path.exists() always returns False for it, and tempfile.mktemp() can
return a name that matches an existing named pipe.

So, tempfile.mktemp() cannot be used to generate unique names for named
pipes. Instead, CreateNamedPipe() should be called in a loop with
different names until it completes successfully.
(cherry picked from commit d6a71f4)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
serhiy-storchaka added a commit that referenced this pull request Feb 24, 2026
…iprocessing and asyncio on Windows (GH-137389) (GH-145170)

Since os.stat() raises an OSError for existing named pipe "\\.\pipe\...",
os.path.exists() always returns False for it, and tempfile.mktemp() can
return a name that matches an existing named pipe.

So, tempfile.mktemp() cannot be used to generate unique names for named
pipes. Instead, CreateNamedPipe() should be called in a loop with
different names until it completes successfully.
(cherry picked from commit d6a71f4)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants