Title: Unfixable potential race conditions on file descriptors in CPython · Issue #121544 · python/cpython · GitHub
Open Graph Title: Unfixable potential race conditions on file descriptors in CPython · Issue #121544 · python/cpython
X Title: Unfixable potential race conditions on file descriptors in CPython · Issue #121544 · python/cpython
Description: Python allows multiple threads to concurrently access file descriptors through files and sockets. Race conditions at the Python level can lead to unexpected behavior, even with the global interpreter lock. Thread sanitizer reports these ...
Open Graph Description: Python allows multiple threads to concurrently access file descriptors through files and sockets. Race conditions at the Python level can lead to unexpected behavior, even with the global interpret...
X Description: Python allows multiple threads to concurrently access file descriptors through files and sockets. Race conditions at the Python level can lead to unexpected behavior, even with the global interpret...
Opengraph URL: https://github.com/python/cpython/issues/121544
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Unfixable potential race conditions on file descriptors in CPython","articleBody":"Python allows multiple threads to concurrently access file descriptors through [files](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files) and [sockets](https://docs.python.org/3/library/socket.html). Race conditions at the Python level can lead to unexpected behavior, even with the global interpreter lock.\r\n\r\nThread sanitizer reports these [races](https://github.com/google/sanitizers/wiki/ThreadSanitizerPopularDataRaces#race-on-a-file-descriptor) in some of our tests that exercise this behavior. We cannot fix these potential race conditions without introducing potential deadlocks.\r\n\r\nFor example, consider:\r\n\r\n```python\r\nimport threading\r\n\r\nsecrets = open(\"secrets.txt\", \"w\");\r\n\r\ndef thread1():\r\n secrets.write(\"a secret\")\r\n\r\ndef thread2():\r\n secrets.close()\r\n\r\ndef thread3():\r\n with open(\"log.txt\", \"w\") as log:\r\n log.write(\"log message\")\r\n\r\nthreading.Thread(target=thread1).start()\r\nthreading.Thread(target=thread2).start()\r\nthreading.Thread(target=thread3).start()\r\n```\r\n\r\nIf you are particularly unlucky, then the file descriptor for `secrets` may be closed by `thread2` and **reused** as the file descriptor for `log` just before `thread1` writes to it. In other words, `thread1` may write \"a secret\" to a completely different file or socket than it intended.\r\n\r\nThis can happen, **even with the GIL**, because the GIL is released around [`write()`](https://github.com/python/cpython/blob/9c08f40a613d9aee78de4ce4ec3e125d1496d148/Python/fileutils.c#L1951-L1972) and [`close()`](https://github.com/python/cpython/blob/9c08f40a613d9aee78de4ce4ec3e125d1496d148/Modules/_io/fileio.c#L122-L128). In other words, the following can happen:\r\n\r\n1. The `secrets.write()` call releases the GIL, but before it actually calls the C `write()` function on the file descriptor....\r\n2. The `secrets.close()` call closes the file descriptor\r\n3. The `open(\"log.txt\", \"w\")` re-uses the same file descriptor number \r\n4. The `secrets.write()` call now `write()` on the wrong file descriptor\r\n\r\nNote that we must release the GIL before calling `write()` or `close()` because these functions potentially block.","author":{"url":"https://github.com/colesbury","@type":"Person","name":"colesbury"},"datePublished":"2024-07-09T17:44:41.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":3},"url":"https://github.com/121544/cpython/issues/121544"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:078dd9d6-1ca4-baf1-d42f-3dbe88e770bf |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 9AA6:CD10D:1AF82DC:22FF6D5:696B34FC |
| html-safe-nonce | 280dc76d1637dc232bfe45f82101c2b13bda1c2224132e78950018a12583d089 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5QUE2OkNEMTBEOjFBRjgyREM6MjJGRjZENTo2OTZCMzRGQyIsInZpc2l0b3JfaWQiOiI3NDUwNDYyNTk4ODYwNTg0MTg4IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 51d72eb87b305f67349102103362cdd62ed020165e8930d03a14cf65e9c0c2b2 |
| hovercard-subject-tag | issue:2398798187 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/python/cpython/121544/issue_layout |
| twitter:image | https://opengraph.githubassets.com/71a2dab6fe568eafe1b6d1e8a43bfe9df54d50c2583bf29f838eb010c3ce5bf4/python/cpython/issues/121544 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/71a2dab6fe568eafe1b6d1e8a43bfe9df54d50c2583bf29f838eb010c3ce5bf4/python/cpython/issues/121544 |
| og:image:alt | Python allows multiple threads to concurrently access file descriptors through files and sockets. Race conditions at the Python level can lead to unexpected behavior, even with the global interpret... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | colesbury |
| hostname | github.com |
| expected-hostname | github.com |
| None | 5f99f7c1d70f01da5b93e5ca90303359738944d8ab470e396496262c66e60b8d |
| turbo-cache-control | no-preview |
| go-import | github.com/python/cpython git https://github.com/python/cpython.git |
| octolytics-dimension-user_id | 1525981 |
| octolytics-dimension-user_login | python |
| octolytics-dimension-repository_id | 81598961 |
| octolytics-dimension-repository_nwo | python/cpython |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 81598961 |
| octolytics-dimension-repository_network_root_nwo | python/cpython |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 82560a55c6b2054555076f46e683151ee28a19bc |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width