Patchwork [2,of,2,stable] worker: prevent cPickle from directly interacting with C FILEs

login
register
mail settings
Submitter Manuel Jacob
Date May 25, 2022, 12:30 a.m.
Message ID <a9e51487cb9a73f871da.1653438630@tmp>
Download mbox | patch
Permalink /patch/50991/
State New
Headers show

Comments

Manuel Jacob - May 25, 2022, 12:30 a.m.
# HG changeset patch
# User Manuel Jacob <me@manueljacob.de>
# Date 1653434314 -7200
#      Wed May 25 01:18:34 2022 +0200
# Branch stable
# Node ID a9e51487cb9a73f871daf0d43a2b6eec9adadbbd
# Parent  d058898bdd462b03c5bff38ad40d1f855ea51c23
# EXP-Topic worker-pickle-load-EINTR
worker: prevent cPickle from directly interacting with C FILEs

Patch

diff --git a/mercurial/worker.py b/mercurial/worker.py
--- a/mercurial/worker.py
+++ b/mercurial/worker.py
@@ -100,6 +100,8 @@ 
             del buf[pos:]
             return bytes(buf)
 
+    _picklereader = _blockingreader
+
 
 else:
 
@@ -111,6 +113,24 @@ 
     def _blockingreader(wrapped):
         return wrapped
 
+    # The following hack is needed to prevent cPickle from using its buggy fast
+    # path for directly interacting with C FILEs. Instead, the file object
+    # should interact with C FILEs. For that, we need to make cPickle think
+    # that we did not actually pass a file object.
+    # CPickle's interaction with FILEs has at least the following two problems:
+    # 1) CPickle does not retry operations on EINTR, while file objects do.
+    # 2) CPickle does not check if an error (e.g. EINTR) happened during
+    #    getc(). Instead, it thinks that EOF was reached.
+
+    class _passthrough(object):
+        def __init__(self, wrapped):
+            self._wrapped = wrapped
+
+        def __getattr__(self, name):
+            return getattr(self._wrapped, name)
+
+    _picklereader = _passthrough
+
 
 if pycompat.isposix or pycompat.iswindows:
     _STARTUP_COST = 0.01
@@ -292,7 +312,7 @@ 
         while openpipes > 0:
             for key, events in selector.select():
                 try:
-                    res = util.pickle.load(_blockingreader(key.fileobj))
+                    res = util.pickle.load(_picklereader(key.fileobj))
                     if hasretval and res[0]:
                         retval.update(res[1])
                     else: