<div style="font-family:Microsoft YaHei;font-size:14px;color:#000000;" class="l_node_has_color"><div data-line="1" data-line-type="context" data-line-index="0" data-selected-line="first">Hello,</div>
<div data-line="2" data-line-type="context" data-line-index="1" data-selected-line=""> </div>
<div data-line="3" data-line-type="context" data-line-index="2" data-selected-line="">read_from_emacs() in pinentry/pinentry-emacs.c retries recv() on every</div>
<div data-line="4" data-line-type="context" data-line-index="3" data-selected-line="">EINTR without checking the timed_out flag. When the pinentry timeout</div>
<div data-line="5" data-line-type="context" data-line-index="4" data-selected-line="">expires, alarm() delivers SIGALRM, catchsig() sets timed_out = 1 and</div>
<div data-line="6" data-line-type="context" data-line-index="5" data-selected-line="">recv() returns EINTR. The do-while loop cannot distinguish this from a</div>
<div data-line="7" data-line-type="context" data-line-index="6" data-selected-line="">SIGWINCH interrupt and retries recv() unconditionally. Since alarm() is</div>
<div data-line="8" data-line-type="context" data-line-index="7" data-selected-line="">one-shot, no further SIGALRM is generated and the retried recv() blocks</div>
<div data-line="9" data-line-type="context" data-line-index="8" data-selected-line="">forever: the timeout mechanism is completely ineffective and pinentry</div>
<div data-line="10" data-line-type="context" data-line-index="9" data-selected-line="">hangs until it is killed.</div>
<div data-line="11" data-line-type="context" data-line-index="10" data-selected-line=""> </div>
<div data-line="12" data-line-type="context" data-line-index="11" data-selected-line="">Reproduction with the default timeout of 60 s:</div>
<div data-line="13" data-line-type="context" data-line-index="12" data-selected-line=""> </div>
<div data-line="14" data-line-type="context" data-line-index="13" data-selected-line="">1. create the fake Emacs socket /tmp/emacs$UID/pinentry</div>
<div data-line="15" data-line-type="context" data-line-index="14" data-selected-line="">2. start pinentry-emacs with INSIDE_EMACS=29.1</div>
<div data-line="16" data-line-type="context" data-line-index="15" data-selected-line="">3. reply "OK\n" to the initial handshake</div>
<div data-line="17" data-line-type="context" data-line-index="16" data-selected-line="">4. send SETDESC, SETPROMPT and GETPIN on stdin</div>
<div data-line="18" data-line-type="context" data-line-index="17" data-selected-line="">5. when pinentry forwards GETPIN to the socket, reply with partial</div>
<div data-line="19" data-line-type="context" data-line-index="18" data-selected-line="">data "D partial\n" but do not send OK or ERR</div>
<div data-line="20" data-line-type="context" data-line-index="19" data-selected-line="">6. wait for the timeout</div>
<div data-line="21" data-line-type="context" data-line-index="20" data-selected-line=""> </div>
<div data-line="22" data-line-type="context" data-line-index="21" data-selected-line="">before: the process is still blocked in recv() after >70 s and has to</div>
<div data-line="23" data-line-type="context" data-line-index="22" data-selected-line="">be killed</div>
<div data-line="24" data-line-type="context" data-line-index="23" data-selected-line="">after : the process returns "ERR 83886142 Timeout" at 60 s</div>
<div data-line="25" data-line-type="context" data-line-index="24" data-selected-line=""> </div>
<div data-line="26" data-line-type="context" data-line-index="25" data-selected-line="">Tested on openEuler 20.03 LTS-SP4 with its /usr/bin/pinentry-emacs</div>
<div data-line="27" data-line-type="context" data-line-index="26" data-selected-line="">(pinentry 1.1.0). The patch is against pinentry-1.3.3 and also applies</div>
<div data-line="28" data-line-type="context" data-line-index="27" data-selected-line="">to 1.1.0, 1.1.1 and 1.2.x.</div>
<div data-line="29" data-line-type="context" data-line-index="28" data-selected-line=""> </div>
<div data-line="30" data-line-type="context" data-line-index="29" data-selected-line="">The fix is minimal and does not change the design: the outer</div>
<div data-line="31" data-line-type="context" data-line-index="30" data-selected-line="">"while (!got_response)" loop is untouched, so data that already arrived</div>
<div data-line="32" data-line-type="context" data-line-index="31" data-selected-line="">is still processed; only the EINTR retry is stopped when SIGALRM fired,</div>
<div data-line="33" data-line-type="context" data-line-index="32" data-selected-line="">and the correct error code is returned.</div>
<div data-line="34" data-line-type="context" data-line-index="33" data-selected-line=""> </div>
<div data-line="35" data-line-type="context" data-line-index="34" data-selected-line="">Note: pinentry-emacs was removed in master by commit 3158644</div>
<div data-line="36" data-line-type="context" data-line-index="35" data-selected-line="">("Remove pinentry-emacs.", 2026-10-27), so 1.3.4+ is not affected. This</div>
<div data-line="37" data-line-type="context" data-line-index="36" data-selected-line="">patch is meant for the 1.3.3 stable line and for downstream</div>
<div data-line="38" data-line-type="context" data-line-index="37" data-selected-line="">distributions that still ship the backend.</div>
<div data-line="39" data-line-type="context" data-line-index="38" data-selected-line=""> </div>
<div data-line="40" data-line-type="context" data-line-index="39" data-selected-line="">Regards,</div>
<div data-line="41" data-line-type="context" data-line-index="40" data-selected-line="">Pei Jiankang</div>
<div data-line="42" data-line-type="context" data-line-index="41" data-selected-line=""> </div>
<div data-line="43" data-line-type="context" data-line-index="42" data-selected-line="">Patch follows:</div>
<div data-line="44" data-line-type="context" data-line-index="43" data-selected-line=""> </div>
<div data-line="45" data-line-type="context" data-line-index="44" data-selected-line="">From 06e534fdd20f9f12aeee02406aa7021d80f1236a Mon Sep 17 00:00:00 2001</div>
<div data-line="46" data-line-type="context" data-line-index="45" data-selected-line="">From: "peijiankang@kylinos.cn" <peijiankang@kylinos.cn></div>
<div data-line="47" data-line-type="context" data-line-index="46" data-selected-line="">Date: Wed, 28 Oct 2026 15:58:47 +0800</div>
<div data-line="48" data-line-type="context" data-line-index="47" data-selected-line="">Subject: [PATCH] Fix timeout race condition in read_from_emacs (CWE-362)</div>
<div data-line="49" data-line-type="context" data-line-index="48" data-selected-line=""> </div>
<div data-line="50" data-line-type="context" data-line-index="49" data-selected-line="">The do-while EINTR retry loop in read_from_emacs does not check the</div>
<div data-line="51" data-line-type="context" data-line-index="50" data-selected-line="">timed_out flag. When alarm() fires SIGALRM, catchsig sets timed_out=1,</div>
<div data-line="52" data-line-type="context" data-line-index="51" data-selected-line="">recv() is interrupted returning EINTR, but the do-while retries recv()</div>
<div data-line="53" data-line-type="context" data-line-index="52" data-selected-line="">unconditionally. Since alarm() is a one-shot timer, no further SIGALRM</div>
<div data-line="54" data-line-type="context" data-line-index="53" data-selected-line="">is generated, and recv() blocks forever, making the timeout mechanism</div>
<div data-line="55" data-line-type="context" data-line-index="54" data-selected-line="">completely ineffective.</div>
<div data-line="56" data-line-type="context" data-line-index="55" data-selected-line=""> </div>
<div data-line="57" data-line-type="context" data-line-index="56" data-selected-line="">Adding !timed_out to the do-while condition allows distinguishing</div>
<div data-line="58" data-line-type="context" data-line-index="57" data-selected-line="">SIGWINCH (should retry) from SIGALRM (should stop). Also return</div>
<div data-line="59" data-line-type="context" data-line-index="58" data-selected-line="">GPG_ERR_TIMEOUT instead of GPG_ERR_ASS_GENERAL when timed_out is set.</div>
<div data-line="60" data-line-type="context" data-line-index="59" data-selected-line=""> </div>
<div data-line="61" data-line-type="context" data-line-index="60" data-selected-line="">Reproduced on openEuler 20.03 LTS-SP4 with /usr/bin/pinentry-emacs:</div>
<div data-line="62" data-line-type="context" data-line-index="61" data-selected-line="">- Before fix: process hangs forever after 60s timeout (recv blocks)</div>
<div data-line="63" data-line-type="context" data-line-index="62" data-selected-line="">- After fix: process returns ERR Timeout after 60s</div>
<div data-line="64" data-line-type="context" data-line-index="63" data-selected-line=""> </div>
<div data-line="65" data-line-type="context" data-line-index="64" data-selected-line="">Signed-off-by: peijiankang@kylinos.cn <peijiankang@kylinos.cn></div>
<div data-line="66" data-line-type="context" data-line-index="65" data-selected-line="">---</div>
<div data-line="67" data-line-type="context" data-line-index="66" data-selected-line="">pinentry/pinentry-emacs.c | 4 +++-</div>
<div data-line="68" data-line-type="context" data-line-index="67" data-selected-line="">1 file changed, 3 insertions(+), 1 deletion(-)</div>
<div data-line="69" data-line-type="context" data-line-index="68" data-selected-line=""> </div>
<div data-line="70" data-line-type="context" data-line-index="69" data-selected-line="">diff --git a/pinentry/pinentry-emacs.c b/pinentry/pinentry-emacs.c</div>
<div data-line="71" data-line-type="context" data-line-index="70" data-selected-line="">index 7cac4a5..dae55e8 100644</div>
<div data-line="72" data-line-type="context" data-line-index="71" data-selected-line="">--- a/pinentry/pinentry-emacs.c</div>
<div data-line="73" data-line-type="context" data-line-index="72" data-selected-line="">+++ b/pinentry/pinentry-emacs.c</div>
<div data-line="74" data-line-type="context" data-line-index="73" data-selected-line="">@@ -352,10 +352,12 @@ read_from_emacs (int s, int timeout, char *buffer, size_t capacity)</div>
<div data-line="75" data-line-type="context" data-line-index="74" data-selected-line="">}</div>
<div data-line="76" data-line-type="context" data-line-index="75" data-selected-line="">/* If we receive a signal (e.g. SIGWINCH, which we pass</div>
<div data-line="77" data-line-type="context" data-line-index="76" data-selected-line="">through to Emacs), on some OSes we get EINTR and must retry. */</div>
<div data-line="78" data-line-type="context" data-line-index="77" data-selected-line="">- while (rl < 0 && errno == EINTR);</div>
<div data-line="79" data-line-type="context" data-line-index="78" data-selected-line="">+ while (rl < 0 && errno == EINTR && !timed_out);</div>
<div data-line="80" data-line-type="context" data-line-index="79" data-selected-line=""> </div>
<div data-line="81" data-line-type="context" data-line-index="80" data-selected-line="">if (rl < 0)</div>
<div data-line="82" data-line-type="context" data-line-index="81" data-selected-line="">{</div>
<div data-line="83" data-line-type="context" data-line-index="82" data-selected-line="">+ if (timed_out)</div>
<div data-line="84" data-line-type="context" data-line-index="83" data-selected-line="">+ return gpg_error (GPG_ERR_TIMEOUT);</div>
<div data-line="85" data-line-type="context" data-line-index="84" data-selected-line="">perror ("recv");</div>
<div data-line="86" data-line-type="context" data-line-index="85" data-selected-line="">return gpg_error (GPG_ERR_ASS_GENERAL);;</div>
<div data-line="87" data-line-type="context" data-line-index="86" data-selected-line="">}</div>
<div data-line="88" data-line-type="context" data-line-index="87" data-selected-line="">--</div>
<div data-line="89" data-line-type="context" data-line-index="88" data-selected-line="">2.25.1</div>
<div data-line-annotation="0,89" data-selected-line="last">
<div data-annotation-content="">
<div data-annotation-slot="">
<div data-prevent-autofocus="">
<div data-prevent-autofocus="">
<div data-component="line-comment-v2" data-variant="editor" data-layout="default">
<div data-slot="line-comment-v2-shell">
<div data-slot="line-comment-v2-input">
<div data-slot="line-comment-v2-field"> </div>
</div>
<div data-slot="line-comment-v2-footer">
<div data-slot="line-comment-v2-footer-actions"> </div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div data-line="90" data-line-type="context" data-line-index="89"> </div>
<p> </p></div>