Mir

Merge lp:~mir-team/mir/handle-mis-shaped-cookies into lp:mir

Proposed by Brandon Schaefer
Status: Rejected
Rejected by: Brandon Schaefer
Proposed branch: lp:~mir-team/mir/handle-mis-shaped-cookies
Merge into: lp:mir
Diff against target: 249 lines (+203/-3)
4 files modified
src/server/input/default_event_builder.cpp (+4/-2)
src/server/input/key_repeat_dispatcher.cpp (+2/-1)
tests/acceptance-tests/CMakeLists.txt (+1/-0)
tests/acceptance-tests/test_server_cookie_authority.cpp (+196/-0)
To merge this branch: bzr merge lp:~mir-team/mir/handle-mis-shaped-cookies
Reviewer Review Type Date Requested Status
Alan Griffiths Disapprove
Daniel van Vugt Abstain
PS Jenkins bot (community) continuous-integration Approve
Mir CI Bot continuous-integration Approve
Andreas Pokorny (community) Approve
Review via email: mp+284952@code.launchpad.net

Commit message

We need to handle mis shaped cookies. If someone creates a cookie authority that returns a Cookie that is too large/small to fit into our own assumption of what a cookie is we need to handle that gracefully.

Description of the change

We need to handle mis shaped cookies. If someone creates a cookie authority that returns a Cookie that is to large/small to fit into our own assumption of what a cookie is we need to handle that gracefully.

If the cookie is NULL. We sigsegv, which will have a meaningful bt.

If the Cookie is smaller then our under the hood version we just copy the size of that.

// This would be a fix me!
If the Cookie is larger then we only copy the max amount we can handle into the internal cookie.

To post a comment you must log in.
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3278
https://mir-jenkins.ubuntu.com/job/mir-ci/225/
Executed test runs:
    None: https://mir-jenkins.ubuntu.com/job/generic-update-mp/225/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/225/rebuild

review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:3278
http://jenkins.qa.ubuntu.com/job/mir-ci/6201/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-android-vivid-i386-build/5795
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-clang-vivid-amd64-build/4702
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-vivid-touch/5751
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-xenial-touch/383
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/525
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/525/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/525
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/525/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5748
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5748/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-touch/8158
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/27301
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/379
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/379/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-xenial-touch/233
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/27302

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/mir-ci/6201/rebuild

review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

The reasoning sounds sensible but how is this related to the linked bug 1536662? Is it that cookies smaller when we can't find entropy?

review: Needs Information
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Its related, as its needed for the USC workaround. There will be another branch that will wait to create the secret but that is also a workaround. As theres no real solution right now.

Soo I can remove the link to that bug but since this branch is needed for the USC fix I figured that it was related

Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

The test names seem to be odd...

Why did you use spin_wait instead of a WaitCondition?

review: Needs Information
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Yes the names do sound strange ... I dont know why i named them like that :)

There was a reason... at some point, but yeah no need and ill fix that!

3279. By Brandon Schaefer

* Fix test names
* Use wait cond vs spin wait

Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

lgtm

review: Approve
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

I'm not sure what we're /really/ trying to support here. (Beyond avoiding a crash.)

If there are "assumptions about cookies" embedded in the wrong code (i.e. code that isn't reconfigurable with the cookie authority), then the problem is the code is in the wrong place!

Almost every time we call cookie->serialize() we have code than immediately copies the result to a cookie::Blob. If cookie::Cookie had a method to populate a blob directly, then that method could be configured by a custom cookie implementation.

Would that address the problem more effectively?

review: Needs Information
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Hmm that would expose the cookie::Blob :(. This is mainly a ... fix until we move to a std::vector. Once we can replace the android input transport (what ill be looking at soon!). Though it would be nice to return a cookie::Blob from a cookie::serialize()... ideally once we have a std::vector everywhere this function will be the expected public interface.

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3279
https://mir-jenkins.ubuntu.com/job/mir-ci/232/
Executed test runs:
    None: https://mir-jenkins.ubuntu.com/job/generic-update-mp/232/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/232/rebuild

review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:3279
http://jenkins.qa.ubuntu.com/job/mir-ci/6208/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-android-vivid-i386-build/5805
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-clang-vivid-amd64-build/4712
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-vivid-touch/5761
    FAILURE: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-xenial-touch/388/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/532
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-amd64-ci/532/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/532
        deb: http://jenkins.qa.ubuntu.com/job/mir-xenial-i386-ci/532/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5758
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-vivid-armhf/5758/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-touch/8168
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/27321
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/384
        deb: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-builder-xenial-armhf/384/artifact/work/output/*zip*/output.zip
    FAILURE: http://jenkins.qa.ubuntu.com/job/mir-mediumtests-runner-xenial-touch/238/console
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/27329

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/mir-ci/6208/rebuild

review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Thanks for the answer.

Next question:
Wouldn't truncating cookie data make it computationally invalid? Like any MAC or checksum would be...

review: Needs Information
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Yes! It would, which is why it wouldnt be supported, but our main use case is sending Empty cookies. This case would fail if we created cookies that were to large for our internal storage but that internal storage *should* hopefully go away when i finish going to captnproto in the input layer so its a vector all the way.

For now its safer to handle with out crashing!

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Yeah /not crashing/ is good. Although you can also "not crash" more elegantly by treating oversized data we can't store as invalid, so store that as empty; length zero or something indicating a definite overflow. Which sounds better than storing truncated (and thus confusingly invalid) data.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

What I'm really saying is that we probably want to be able to tell the difference between a failed malicious spoofing attempt (invalid cookie), and non-malicious truncation (invalid cookie).

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Let me try paraphrasing the case for this MP to see if I've got it:

1. mir::cookie::Blob isn't part of the mircookie, it is a temporary convenience for the event serialization code. (OTOH mir::cookie::default_blob_size from the same private header is used in mircookie.)

2. We don't provide a conversion (ctor, function, etc.) between std::vector<uint8_t> and mir::cookie::Blob but instead repeat the logic wherever we need this conversion.

3. The crash happens because this temporary convenience makes assumptions about serialized cookies that are invalidated by USC's replacement for CookieAuthority.

But...

4. This is a temporary mess to be cleaned up as part of a rework of the aforementioned event serialization code.

~~~~

If I've got that right, isn't a simpler solution to have USC's replacement for Cookie::serialize() return a dummy vector of the expected size?

review: Needs Information
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Yes that was one quick fix I thought about ... which im 100% fine with. Just thought this would be better :). Im more then happy to have this quick hack in USC vs this large change/hack here.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Enough blocking from me.

It certainly seems like an improvement, even if some people want a different improvement.

review: Abstain
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> Yes that was one quick fix I thought about ... which im 100% fine with. Just
> thought this would be better :). Im more then happy to have this quick hack in
> USC vs this large change/hack here.

+1

The promised changes to event serialization should cover the preexisting encapsulation issues (as discussed).

review: Disapprove

Unmerged revisions

3279. By Brandon Schaefer

* Fix test names
* Use wait cond vs spin wait

3278. By Brandon Schaefer

* Dont change that space

3277. By Brandon Schaefer

* Test we can override a stub cookie factory and accept cookie size thats less then our internal storage size

3276. By Brandon Schaefer

* The current test, some strange hangup on a surface release or thread join (if you skip the release)

3275. By Brandon Schaefer

* We throw on NULL cookies and handle empty/large cookies now

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/server/input/default_event_builder.cpp'
2--- src/server/input/default_event_builder.cpp 2016-01-29 08:18:22 +0000
3+++ src/server/input/default_event_builder.cpp 2016-02-04 13:40:07 +0000
4@@ -1,5 +1,5 @@
5 /*
6- * Copyright © 2015 Canonical Ltd.
7+ * Copyright © 2015-2016 Canonical Ltd.
8 *
9 * This program is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License version 3,
11@@ -54,8 +54,10 @@
12 if (action == mir_touch_action_up || action == mir_touch_action_down)
13 {
14 auto const cookie = cookie_authority->make_cookie(event.motion.event_time.count());
15+
16 auto const serialized_cookie = cookie->serialize();
17- std::copy_n(std::begin(serialized_cookie), event.motion.cookie.size(), std::begin(event.motion.cookie));
18+ auto const bytes_to_copy = std::min(serialized_cookie.size(), event.motion.cookie.size());
19+ std::copy_n(std::begin(serialized_cookie), bytes_to_copy, std::begin(event.motion.cookie));
20 }
21
22 me::add_touch(event, touch_id, action, tooltype, x_axis_value, y_axis_value, pressure_value, touch_major_value,
23
24=== modified file 'src/server/input/key_repeat_dispatcher.cpp'
25--- src/server/input/key_repeat_dispatcher.cpp 2016-01-29 08:18:22 +0000
26+++ src/server/input/key_repeat_dispatcher.cpp 2016-02-04 13:40:07 +0000
27@@ -122,7 +122,8 @@
28 ev.key.event_time = std::chrono::steady_clock::now().time_since_epoch();
29 auto const cookie = cookie_authority->make_cookie(ev.key.event_time.count());
30 auto const serialized_cookie = cookie->serialize();
31- std::copy_n(std::begin(serialized_cookie), ev.key.cookie.size(), std::begin(ev.key.cookie));
32+ auto const bytes_to_copy = std::min(serialized_cookie.size(), ev.key.cookie.size());
33+ std::copy_n(std::begin(serialized_cookie), bytes_to_copy, std::begin(ev.key.cookie));
34 next_dispatcher->dispatch(ev);
35
36 capture_alarm->reschedule_in(repeat_delay);
37
38=== modified file 'tests/acceptance-tests/CMakeLists.txt'
39--- tests/acceptance-tests/CMakeLists.txt 2016-01-29 08:18:22 +0000
40+++ tests/acceptance-tests/CMakeLists.txt 2016-02-04 13:40:07 +0000
41@@ -51,6 +51,7 @@
42 test_surface_specification.cpp
43 test_system_compositor_window_manager.cpp
44 test_session_mediator_report.cpp
45+ test_server_cookie_authority.cpp
46 test_surface_raise.cpp
47 test_client_cookie.cpp
48 )
49
50=== added file 'tests/acceptance-tests/test_server_cookie_authority.cpp'
51--- tests/acceptance-tests/test_server_cookie_authority.cpp 1970-01-01 00:00:00 +0000
52+++ tests/acceptance-tests/test_server_cookie_authority.cpp 2016-02-04 13:40:07 +0000
53@@ -0,0 +1,196 @@
54+/*
55+ * Copyright © 2016 Canonical Ltd.
56+ *
57+ * This program is free software: you can redistribute it and/or modify
58+ * it under the terms of the GNU General Public License version 3 as
59+ * published by the Free Software Foundation.
60+ *
61+ * This program is distributed in the hope that it will be useful,
62+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
63+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
64+ * GNU General Public License for more details.
65+ *
66+ * You should have received a copy of the GNU General Public License
67+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
68+ *
69+ * Authored by: Brandon Schaefer <brandon.schaefer@canonical.com>
70+ */
71+
72+#include "mir_test_framework/stub_server_platform_factory.h"
73+#include "mir_test_framework/connected_client_with_a_surface.h"
74+#include "mir/test/wait_condition.h"
75+
76+#include "mir/input/input_device_info.h"
77+#include "mir_test_framework/fake_input_device.h"
78+
79+#include "mir/cookie/cookie.h"
80+#include "mir/cookie/authority.h"
81+
82+#include <linux/input.h>
83+
84+namespace mtf = mir_test_framework;
85+namespace mt = mir::test;
86+namespace mi = mir::input;
87+namespace mis = mir::input::synthesis;
88+
89+namespace
90+{
91+std::chrono::seconds const max_wait{4};
92+void event_capturing_callback(MirSurface*, MirEvent const* ev, void* ctx);
93+}
94+
95+class EmptyCookie : public mir::cookie::Cookie
96+{
97+public:
98+ virtual uint64_t timestamp() const override
99+ {
100+ return 0;
101+ }
102+
103+ virtual std::vector<uint8_t> serialize() const override
104+ {
105+ return std::vector<uint8_t>();
106+ }
107+};
108+
109+class EmptyCookieAuthority : public mir::cookie::Authority
110+{
111+public:
112+ virtual std::unique_ptr<mir::cookie::Cookie> make_cookie(uint64_t const& /*timestamp*/) override
113+ {
114+ return std::make_unique<EmptyCookie>();
115+ }
116+
117+ virtual std::unique_ptr<mir::cookie::Cookie> make_cookie(std::vector<uint8_t> const& /*raw_cookie*/) override
118+ {
119+ return std::make_unique<EmptyCookie>();
120+ }
121+};
122+
123+struct FocusExposeSurface : mtf::ConnectedClientWithASurface
124+{
125+ void SetUp() override
126+ {
127+ mtf::ConnectedClientWithASurface::SetUp();
128+
129+ // Need fullscreen for the cursor events
130+ auto const spec = mir_connection_create_spec_for_changes(connection);
131+ mir_surface_spec_set_fullscreen_on_output(spec, 1);
132+ mir_surface_apply_spec(surface, spec);
133+ mir_surface_spec_release(spec);
134+
135+ mir_surface_set_event_handler(surface, &event_capturing_callback, this);
136+ mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface));
137+
138+ ready_to_accept_events.wait_for_at_most_seconds(max_wait);
139+ if (!ready_to_accept_events.woken())
140+ BOOST_THROW_EXCEPTION(std::runtime_error("Timeout waiting for surface to become focused and exposed"));
141+ }
142+
143+ std::unique_ptr<mtf::FakeInputDevice> fake_keyboard{
144+ mtf::add_fake_input_device(mi::InputDeviceInfo{"keyboard", "keyboard-uid" , mi::DeviceCapability::keyboard})
145+ };
146+ std::unique_ptr<mtf::FakeInputDevice> fake_pointer{
147+ mtf::add_fake_input_device(mi::InputDeviceInfo{"mouse", "mouse-uid" , mi::DeviceCapability::pointer})
148+ };
149+ std::unique_ptr<mtf::FakeInputDevice> fake_touch_screen{
150+ mtf::add_fake_input_device(mi::InputDeviceInfo{
151+ "touch screen", "touch-screen-uid", mi::DeviceCapability::touchscreen | mi::DeviceCapability::multitouch})
152+ };
153+
154+ mir::test::WaitCondition ready_to_accept_events;
155+ mir::test::WaitCondition cookie_event_received;
156+ mutable std::mutex mutex;
157+ bool exposed{false};
158+ bool focused{false};
159+};
160+
161+struct EmptyCookieAuthorityOverride : FocusExposeSurface
162+{
163+ EmptyCookieAuthorityOverride()
164+ {
165+ server.override_the_cookie_authority([this] ()
166+ { return std::make_unique<EmptyCookieAuthority>(); });
167+ }
168+};
169+
170+namespace
171+{
172+void event_capturing_callback(MirSurface* /*surface*/, MirEvent const* ev, void* ctx)
173+{
174+ auto const event_type = mir_event_get_type(ev);
175+ auto focused_surface = static_cast<FocusExposeSurface*>(ctx);
176+
177+ if (event_type == mir_event_type_surface)
178+ {
179+ auto event = mir_event_get_surface_event(ev);
180+ auto const attrib = mir_surface_event_get_attribute(event);
181+ auto const value = mir_surface_event_get_attribute_value(event);
182+
183+ std::lock_guard<std::mutex> lk(focused_surface->mutex);
184+
185+ if (attrib == mir_surface_attrib_visibility &&
186+ value == mir_surface_visibility_exposed)
187+ {
188+ focused_surface->exposed = true;
189+ }
190+
191+ if (attrib == mir_surface_attrib_focus &&
192+ value == mir_surface_focused)
193+ {
194+ focused_surface->focused = true;
195+ }
196+
197+ if (focused_surface->exposed && focused_surface->focused && !focused_surface->ready_to_accept_events.woken())
198+ {
199+ focused_surface->ready_to_accept_events.wake_up_everyone();
200+ }
201+ }
202+ else if (event_type == mir_event_type_input)
203+ {
204+ auto const* iev = mir_event_get_input_event(ev);
205+
206+ std::lock_guard<std::mutex> lk(focused_surface->mutex);
207+ if (mir_input_event_has_cookie(iev))
208+ {
209+ focused_surface->cookie_event_received.wake_up_everyone();
210+ }
211+ }
212+}
213+}
214+
215+TEST_F(EmptyCookieAuthorityOverride, touch_events_handle_empty_cookie)
216+{
217+ fake_touch_screen->emit_event(
218+ mis::a_touch_event()
219+ .at_position({0, 0})
220+ );
221+
222+ fake_touch_screen->emit_event(
223+ mis::a_touch_event()
224+ .with_action(mis::TouchParameters::Action::Move)
225+ .at_position({1, 1})
226+ );
227+
228+ cookie_event_received.wait_for_at_most_seconds(max_wait);
229+ EXPECT_TRUE(cookie_event_received.woken());
230+}
231+
232+TEST_F(EmptyCookieAuthorityOverride, pointer_events_handle_empty_cookie)
233+{
234+ fake_pointer->emit_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
235+ fake_pointer->emit_event(mis::a_button_up_event().of_button(BTN_LEFT));
236+
237+ cookie_event_received.wait_for_at_most_seconds(max_wait);
238+ EXPECT_TRUE(cookie_event_received.woken());
239+}
240+
241+TEST_F(EmptyCookieAuthorityOverride, key_events_handle_empty_cookie)
242+{
243+ fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_M));
244+
245+ cookie_event_received.wait_for_at_most_seconds(max_wait);
246+ EXPECT_TRUE(cookie_event_received.woken());
247+}
248+
249+

Subscribers

People subscribed via source and target branches