Package: gnupg-agent
Version: 2.0.9-3
Severity: critical

gnupg-agent somehow manages to change the SigBlk mask causing all child
processes that are spawned part of the X session to have the SigBlk
mask set causing real-time signals to be blocked. This breaks all
applications spawned in that X session that rely on
real-time signals.

As this breaks unrelated software I set the severity to critical.

Here the summary of my full investigation:
About 2 months ago I started to suffer from an issue that first looked
like a bug in Mono's threading implementation. The test-case to
reproduce it looks like this:

Attachment:
test-thread-abort.cs

Compile:
mcs test-thread-abort.cs

Run:
mono test-thread-abort.exe

My output:
thread.Start()
Worker(): thread started
Worker(): working... 0
Worker(): working... 1
Worker(): working... 2
Worker(): working... 3
Worker(): working... 4
thread.Abort()
press enter to quit!
Worker(): working... 5
Worker(): working... 6
Worker(): working... 7
Worker(): working... 8
Worker(): working... 9
Worker(): working... 10
Worker(): working... 11
Worker(): working... 12
(continuing forever -> killed using ^C)

The expected output:
thread.Start()
Worker(): thread started
Worker(): working... 0
Worker(): working... 1
Worker(): working... 2
Worker(): working... 3
Worker(): working... 4
thread.Abort()
press enter to quit!
Worker(): thread aborted!
Worker(): thread ended

The thread was never aborted and thus forever continued on my system.
After more investigation in Mono's threading implementation I found
that it uses real-time signals to control the threads. A strace then
showed that the signal (SIGRT1) was send but never received.... This
made me really curious.

So I wrote a C test-case for this issue, which looks like this:

Attachment:
test-signal.c

Compile:
gcc test-signal.c -o test-signal

Run:
./test-signal

My Output:
SIGRTMIN: 34
sigismember(): 1
SIGRTMIN is blocked, trying to unblock...done.
sigismember(): 0
SIGRT_1 received
SIGRT_2 received
SIGQUIT received

The expected output:
SIGRTMIN: 34
sigismember(): 0
SIGRT_1 received
SIGRT_2 received
SIGQUIT received

And that proved the signal (SIGRT) is indeed already blocked when the
application is started.

Then I found out / learned that blocking signals is a feature and the
SigBlk mask is inherited to child processes. So something was setting
this mask, and I could not find out what it was till today.

I reinstalled my system (1 week ago) after I could not find the exact
cause of this issue (after 3 continuous days of investigation)
and today it started to happen again, after I was sponsoring a package
upload, for which I had to install pscd and gnupg-agent to get my
crypto card working again (after the reinstall).

Then I disabled the gpg-agent in my ~/.gnupg/gpg.conf, restarted my X
session and viola SigBlk mask is back to normal and all my lovely
applications work again like they should.

Enabling the gpg-agent in ~/.gnupg/gpg.conf again brings the issue back.

I noticed the gpg-agent is started in /etc/X11/Xsession.d/90gpg-agent,
but I no have no idea how it manages to change (or rather inherits) the
SigBlk mask of other processes spawned in the X session....

Here also a little detection script I wrote to identify processes with
that set SigBlk mask (it also finds some "false-positives" as some apps
really need that mask, but not _all_ processes launched under the X
session!)

Attachment:
proc-sigblk.sh:

My output:
Name:   x-session-manag
Pid:    21699
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   dbus-launch
Pid:    21757
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   dbus-daemon
Pid:    21758
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   seahorse-agent
Pid:    21764
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   gnome-settings-
Pid:    21769
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   metacity
Pid:    21781
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   gnome-panel
Pid:    21782
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   nautilus
Pid:    21783
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   gnome-screensav
Pid:    21786
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   gnome-vfs-daemo
Pid:    21795
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   bluetooth-apple
Pid:    21798
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   gnome-do
Pid:    21799
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   update-notifier
Pid:    21803
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   gnome-do
Pid:    21804
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   system-config-p
Pid:    21811
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   kerneloops-appl
Pid:    21815
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   gnome-volume-ma
Pid:    21817
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   nm-applet
Pid:    21819
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   gnome-power-man
Pid:    21827
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   multiload-apple
Pid:    21849
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   mixer_applet2
Pid:    21852
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   gweather-applet
Pid:    21855
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   locate
Pid:    21862
Uid:    1000    1000    1000    1000
Gid:    1000    105     105     105
Name:   locate
Pid:    21863
Uid:    1000    1000    1000    1000
Gid:    1000    105     105     105
Name:   mapping-daemon
Pid:    21866
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   gnome-terminal
Pid:    21868
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   gnome-pty-helpe
Pid:    21871
Uid:    1000    1000    1000    1000
Gid:    1000    43      43      43
Name:   bonobo-activati
Pid:    3878
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Name:   egrep
Pid:    22046
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000

The expected output:
(none or a very small set of applications like gconfd-2 or some
gnome applets)

-- 
Regards,

Mirco 'meebey' Bauer

PGP-Key ID: 0xEEF946C8

FOSS Developer    [EMAIL PROTECTED]  http://www.meebey.net/
PEAR Developer    [EMAIL PROTECTED]     http://pear.php.net/
Debian Developer  [EMAIL PROTECTED]  http://www.debian.org/
#include <stdio.h>
#include <signal.h>
 
void sigint_handler(void);
void sigquit_handler(void); 
void sigrt1_handler(void); 
void sigrt2_handler(void); 
void sigrt3_handler(void); 
void sigpwr_handler(void); 
 
main()
{
		printf("SIGRTMIN: %i\n", SIGRTMIN);
		
		sigset_t set, old_set, new_set;
		int ismember;
		
		if (sigprocmask(SIG_SETMASK, NULL, &old_set) != 0)
			printf("sigprocmask() failed!\n");
		
		ismember = sigismember(&old_set, SIGRTMIN);
		printf("sigismember(): %i\n", ismember);
		if (ismember) {
			printf("SIGRTMIN is blocked, trying to unblock...");
			sigemptyset(&new_set);
			sigaddset(&new_set, SIGRTMIN);
			sigaddset(&new_set, SIGRTMIN+1);
			if (sigprocmask(SIG_UNBLOCK, &new_set, NULL) != 0) {
				 printf("failed!\n");
				 exit(1);
			}
			
			printf("done.\n");
			
			sigemptyset(&set);
			ismember = sigismember(&set, SIGRTMIN);
			printf("sigismember(): %i\n", ismember);
		}
		
		signal(SIGINT, sigint_handler);
		signal(SIGQUIT, sigquit_handler);
		signal(SIGRTMIN, sigrt1_handler);
		signal(SIGRTMIN+1, sigrt2_handler);
		signal(SIGRTMIN+2, sigrt3_handler);
		signal(SIGPWR, sigpwr_handler);

		kill(getpid(), SIGRTMIN);
		kill(getpid(), SIGRTMIN+1);
		
		kill(getpid(), SIGQUIT);
		
		for(;;); /* infinite loop */
}
 
void sigint_handler(void)
{
		signal(SIGINT, sigint_handler);
		/* NOTE some versions of UNIX will reset signal to default
		after each call. So for portability reset signal each time */
 
		printf("SIGINT received\n");
}
 
void sigquit_handler(void)
{
		printf("SIGQUIT received\n");
		exit(0); /* normal exit status */
}

void sigrt1_handler(void)
{
		printf("SIGRT_1 received\n");
}

void sigrt2_handler(void)
{
		printf("SIGRT_2 received\n");
}

void sigrt3_handler(void)
{
		printf("SIGRT_3 received\n");
}

void sigpwr_handler(void)
{
		printf("SIGPWR received\n");
}
using System;
using System.Threading;
using System.Runtime.InteropServices;

class MainClass
{
    static int loopCount;

    static void Main()
    {
        Thread thread = new Thread(new ThreadStart(Worker));
        Console.WriteLine("thread.Start()");
        thread.IsBackground = true;
        thread.Start();

        // give the thread some time to spawn
        Thread.Sleep(5000);

        Console.WriteLine("thread.Abort()");
        thread.Abort();

        Console.WriteLine("press enter to quit!");
        Console.ReadLine();
    }

    static void Worker()
    {
        Console.WriteLine("Worker(): thread started");
        try {
            while (true) {
                Console.WriteLine("Worker(): working... " + loopCount++); Thread.Sleep(1000);
            }
        } catch (ThreadAbortException) {
            Thread.ResetAbort();
            Console.WriteLine("Worker(): thread aborted!");
        }
        Console.WriteLine("Worker(): thread ended");
    }
}

Attachment: proc-sigblk.sh
Description: application/shellscript

Reply via email to