Hello again,
Unfortunately, a real solution for the problem is not as simple as that. You are right that the badge is used as a key for looking up the signal-context within core. But by passing the badge as plain data instead of a capability, the referred signal context could be forged by the client. This way, a malicious client would be able to submit signals to all signal receivers in the system. The use of capabilities prevents that.
replying to myself now... .-)
I just had the following idea for a fix: When unmarshalling a capability (in 'Ipc_istream::_unmarshal_capability') we need to distinguish the case of having got a new capability from the case of getting a reference to an already known capability. We receive the 'unique_id' as hint (it is just plain data - hence untrusted information) about which capability selector the argument refers to. Using this 'unique_id', we could look up the core-local capability selector at the cap-selector allocator. Currently, we do not store the unique ID at this allocator. So we would need to add a way to register the unique IDs that correspond to cap selectors and a way to perform a lookup from cap selector to unique ID.
If the lookup fails, we know that we received a new capability (for the signal service, this should never happen because all signal contexts are allocated at core). If the lookup succeeds, we obtained a capability selector that can now be tested via the kernel's 'l4_task_cap_equal' kernel function. If the just received capability selector refers to the same kernel capability as the looked up selector, we just keep using the one returned by the lookup and do not allocate a new selector.
Of course there is still the other part of the problem: keeping a reference count for each capability selector (similarly to how shared pointers work). But the fix above should actually solve the capability-selector leak in the packet-stream case without introducing a security problem.
Cheers Norman