• Eric W. Biederman's avatar
    signal: Don't restart fork when signals come in. · c3ad2c3b
    Eric W. Biederman authored
    Wen Yang <wen.yang99@zte.com.cn> and majiang <ma.jiang@zte.com.cn>
    report that a periodic signal received during fork can cause fork to
    continually restart preventing an application from making progress.
    The code was being overly pessimistic.  Fork needs to guarantee that a
    signal sent to multiple processes is logically delivered before the
    fork and just to the forking process or logically delivered after the
    fork to both the forking process and it's newly spawned child.  For
    signals like periodic timers that are always delivered to a single
    process fork can safely complete and let them appear to logically
    delivered after the fork().
    While examining this issue I also discovered that fork today will miss
    signals delivered to multiple processes during the fork and handled by
    another thread.  Similarly the current code will also miss blocked
    signals that are delivered to multiple process, as those signals will
    not appear pending during fork.
    Add a list of each thread that is currently forking, and keep on that
    list a signal set that records all of the signals sent to multiple
    processes.  When fork completes initialize the new processes
    shared_pending signal set with it.  The calculate_sigpending function
    will see those signals and set TIF_SIGPENDING causing the new task to
    take the slow path to userspace to handle those signals.  Making it
    appear as if those signals were received immediately after the fork.
    It is not possible to send real time signals to multiple processes and
    exceptions don't go to multiple processes, which means that that are
    no signals sent to multiple processes that require siginfo.  This
    means it is safe to not bother collecting siginfo on signals sent
    during fork.
    The sigaction of a child of fork is initially the same as the
    sigaction of the parent process.  So a signal the parent ignores the
    child will also initially ignore.  Therefore it is safe to ignore
    signals sent to multiple processes and ignored by the forking process.
    Signals sent to only a single process or only a single thread and delivered
    during fork are treated as if they are received after the fork, and generally
    not dealt with.  They won't cause any problems.
    V2: Added removal from the multiprocess list on failure.
    V3: Use -ERESTARTNOINTR directly
    V4: - Don't queue both SIGCONT and SIGSTOP
        - Initialize signal_struct.multiprocess in init_task
        - Move setting of shared_pending to before the new task
          is visible to signals.  This prevents signals from comming
          in before shared_pending.signal is set to delayed.signal
          and being lost.
    V5: - rework list add and delete to account for idle threads
    v6: - Use sigdelsetmask when removing stop signals
    Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=200447
    Reported-by: Wen Yang <wen.yang99@zte.com.cn> and
    Reported-by: default avatarmajiang <ma.jiang@zte.com.cn>
    Fixes: 4a2c7a78 ("[PATCH] make fork() atomic wrt pgrp/session signals")
    Signed-off-by: default avatar"Eric W. Biederman" <ebiederm@xmission.com>