[RFC wayland] wayland-server: assert instead of posting events with wrong client objects
Derek Foreman
derekf at osg.samsung.com
Thu Dec 8 19:28:41 UTC 2016
On 02/12/16 02:07 AM, Pekka Paalanen wrote:
> On Thu, 1 Dec 2016 17:19:41 -0600
> Derek Foreman <derekf at osg.samsung.com> wrote:
>
>> Check that all the objects in an event belong to the same client as
>> the resource posting it. This prevents a compositor from accidentally
>> mixing client objects and posting an event that causes a client to
>> kill itself.
>>
>> It's intended that the compositor killing itself be easier to debug
>> than the client killing itself for something that it's completely
>> innocent of.
>>
>> Signed-off-by: Derek Foreman <derekf at osg.samsung.com>
>> ---
>>
>> Recently caught a bug in Enlightenment where we were posting keyboard enter
>> events with a surface from another client. In most cases this results in
>> the client disconnecting itself due to the target object being the wrong type.
>> It might be more hilarious if the types just magically happened to match.
>>
>> Are we interested in this level of footgun protection?
>>
>> It's also been suggested that this might be better if conditional on
>> WAYLAND_DEBUG.
>
> Hi Derek,
>
> this is something I have been wanting for a long time. So far I only
> wrote down what we should have with libwayland-client:
> https://phabricator.freedesktop.org/T50
That is a nice list. :)
Glad I'm not the only one that wants this.
> IMO, your check should not be an assert, it should be an unconditional
> abort() with no way to disable it and a clear log message. Like a
> segfault is, only better.
>
> A compositor that handles SEGVs and ABRTs will then also provide a
> backtrace that should be very helpful in squashing the bug.
>
> However, it seems our existing convention is to just kill the client on
> compositor programming errors (e.g. wl_closure_marshal() failing on
> nullable violation). I don't really like that, but it's a precedent and
> the very least we can detect the problem and yell rather than have
> clients mysteriously fail.
I think it would be trivial to catch nullable violation from the
compositor in the same place where I'm testing for accidental mixing of
client objects.
I don't think we should remove any client side sanity checks, but adding
that check on the compositor side seems like a win for debugging to me.
Should I do this too?
> libwayland-client does call wl_abort() on such failures.
>
>> src/wayland-server.c | 23 +++++++++++++++++++++++
>> 1 file changed, 23 insertions(+)
>>
>> diff --git a/src/wayland-server.c b/src/wayland-server.c
>> index 9d7d9c1..bf1719a 100644
>> --- a/src/wayland-server.c
>> +++ b/src/wayland-server.c
>> @@ -160,6 +160,27 @@ log_closure(struct wl_resource *resource,
>> }
>> }
>>
>> +static bool
>> +clients_match(struct wl_resource *resource, uint32_t opcode,
>> + union wl_argument *args)
>> +{
>> + struct wl_object *object = &resource->object;
>> + const char *sig = object->interface->events[opcode].signature;
>> +
>> + for (;*sig;sig++) {
>> + if (*sig == '?')
>> + continue;
>
> How about skipping version numbers as well?
Are there actually version numbers that have a place in the signature
but no associated argument? My read of get_next_argument left me
thinking that only ? was present without an argument.
This is really an academic question because...
> I believe there should be similar parsing functions elsewhere to either
> follow or even reuse/refactor.
Yes, there's get_next_argument, which I've re-written my patch to use.
So I think that should handle any appropriate skipping that I may have
otherwise missed?
>> +
>> + if (*sig == 'o') {
>
> I think you would also want to handle new_id args. Events with new_id
> are rare, but they do exist (wl_data_device.data_offer).
I'm not sure how to do this. new_id will just give me a number, and I
think it would be complicated to ensure that the id is for the correct
client?
I wouldn't mind putting this down for "further work at a later date". :)
I'll follow up shortly with a new patch.
Thanks,
Derek
>> + struct wl_resource *res = (struct wl_resource *) (args->o);
>> + if (res && res->client != resource->client)
>> + return false;
>> + }
>> + args++;
>> + }
>> + return true;
>> +}
>> +
>> WL_EXPORT void
>> wl_resource_post_event_array(struct wl_resource *resource, uint32_t opcode,
>> union wl_argument *args)
>> @@ -167,6 +188,7 @@ wl_resource_post_event_array(struct wl_resource *resource, uint32_t opcode,
>> struct wl_closure *closure;
>> struct wl_object *object = &resource->object;
>>
>> + assert(clients_match(resource, opcode, args));
>> closure = wl_closure_marshal(object, opcode, args,
>> &object->interface->events[opcode]);
>>
>> @@ -206,6 +228,7 @@ wl_resource_queue_event_array(struct wl_resource *resource, uint32_t opcode,
>> struct wl_closure *closure;
>> struct wl_object *object = &resource->object;
>>
>> + assert(clients_match(resource, opcode, args));
>> closure = wl_closure_marshal(object, opcode, args,
>> &object->interface->events[opcode]);
>>
>
> A good idea, all in all.
>
>
> Thanks,
> pq
>
More information about the wayland-devel
mailing list