diff mbox series

[BlueZ,v2,2/2] input/hog: Do not create UHID if report map is broken

Message ID 20210122001326.14263-2-sonnysasaka@chromium.org (mailing list archive)
State New, archived
Headers show
Series [BlueZ,v2,1/2] input/hog: Fix double registering report value callbacks | expand

Commit Message

Sonny Sasaka Jan. 22, 2021, 12:13 a.m. UTC
The report map in the cache could be dirty, for example when reading a
report map from peer was cancelled, we should be able to detect it and
not try to create UHID. Instead we will read it again from the peer.

---
 profiles/input/hog-lib.c | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)

Comments

Luiz Augusto von Dentz Jan. 22, 2021, 12:36 a.m. UTC | #1
Hi Sonny,

On Thu, Jan 21, 2021 at 4:18 PM Sonny Sasaka <sonnysasaka@chromium.org> wrote:
>
> The report map in the cache could be dirty, for example when reading a
> report map from peer was cancelled, we should be able to detect it and
> not try to create UHID. Instead we will read it again from the peer.

Don't we clean the cache if it had failed? Or you meant to say the
read long procedure was not complete so we got just part of the report
map? In that case we should have failed, also if we need to protect
uhid from malformed report map, which sounds like a kernel bug, then
we should at least have it inside bt_uhid instance so we can at least
attempt to have some unit testing done with broken report maps.

> ---
>  profiles/input/hog-lib.c | 21 ++++++++++++++++++---
>  1 file changed, 18 insertions(+), 3 deletions(-)
>
> diff --git a/profiles/input/hog-lib.c b/profiles/input/hog-lib.c
> index 089f42826..d6a3bda4d 100644
> --- a/profiles/input/hog-lib.c
> +++ b/profiles/input/hog-lib.c
> @@ -946,7 +946,7 @@ static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
>         struct uhid_event ev;
>         ssize_t vlen = report_map_len;
>         char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */
> -       int i, err;
> +       int i, err, collection_depth = 0;
>         GError *gerr = NULL;
>
>         DBG("Report MAP:");
> @@ -960,6 +960,14 @@ static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
>                         if (!long_item && (value[i] & 0xfc) == 0x84)
>                                 hog->has_report_id = TRUE;
>
> +                       // Start Collection
> +                       if (value[i] == 0xa1)
> +                               collection_depth++;
> +
> +                       // End Collection
> +                       if (value[i] == 0xc0)
> +                               collection_depth--;
> +
>                         DBG("\t%s", item2string(itemstr, &value[i], ilen));
>
>                         i += ilen;
> @@ -968,10 +976,15 @@ static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
>
>                         /* Just print remaining items at once and break */
>                         DBG("\t%s", item2string(itemstr, &value[i], vlen - i));
> -                       break;
> +                       return;
>                 }
>         }
>
> +       if (collection_depth != 0) {
> +               error("Report Map error: unbalanced collection");
> +               return;
> +       }
> +
>         /* create uHID device */
>         memset(&ev, 0, sizeof(ev));
>         ev.type = UHID_CREATE;
> @@ -1365,7 +1378,9 @@ static void foreach_hog_chrc(struct gatt_db_attribute *attr, void *user_data)
>                          * UHID to optimize reconnection.
>                          */
>                         uhid_create(hog, report_map.value, report_map.length);
> -               } else {
> +               }
> +
> +               if (!hog->uhid_created) {
>                         read_char(hog, hog->attrib, value_handle,
>                                                 report_map_read_cb, hog);
>                 }
> --
> 2.29.2
>
Sonny Sasaka Jan. 22, 2021, 1:24 a.m. UTC | #2
Hi Luiz,

On Thu, Jan 21, 2021 at 4:37 PM Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
>
> Hi Sonny,
>
> On Thu, Jan 21, 2021 at 4:18 PM Sonny Sasaka <sonnysasaka@chromium.org> wrote:
> >
> > The report map in the cache could be dirty, for example when reading a
> > report map from peer was cancelled, we should be able to detect it and
> > not try to create UHID. Instead we will read it again from the peer.
>
> Don't we clean the cache if it had failed? Or you meant to say the
> read long procedure was not complete so we got just part of the report
> map?
Looks like this is the case. It happened to me once when I cancel
reconnection (trigger pairing mode during reconnection) from the
keyboard side. It's hard to confirm since I have to get the timing
right.

> In that case we should have failed
I agree. However it seems that the code already tries to fail by
looking at the status inside report_map_read_cb, but somehow it still
gets through. It could be the keyboard bug that we have to detect
anyway?

> also if we need to protect
> uhid from malformed report map, which sounds like a kernel bug, then
> we should at least have it inside bt_uhid instance so we can at least
> attempt to have some unit testing done with broken report maps.
>
> > ---
> >  profiles/input/hog-lib.c | 21 ++++++++++++++++++---
> >  1 file changed, 18 insertions(+), 3 deletions(-)
> >
> > diff --git a/profiles/input/hog-lib.c b/profiles/input/hog-lib.c
> > index 089f42826..d6a3bda4d 100644
> > --- a/profiles/input/hog-lib.c
> > +++ b/profiles/input/hog-lib.c
> > @@ -946,7 +946,7 @@ static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
> >         struct uhid_event ev;
> >         ssize_t vlen = report_map_len;
> >         char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */
> > -       int i, err;
> > +       int i, err, collection_depth = 0;
> >         GError *gerr = NULL;
> >
> >         DBG("Report MAP:");
> > @@ -960,6 +960,14 @@ static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
> >                         if (!long_item && (value[i] & 0xfc) == 0x84)
> >                                 hog->has_report_id = TRUE;
> >
> > +                       // Start Collection
> > +                       if (value[i] == 0xa1)
> > +                               collection_depth++;
> > +
> > +                       // End Collection
> > +                       if (value[i] == 0xc0)
> > +                               collection_depth--;
> > +
> >                         DBG("\t%s", item2string(itemstr, &value[i], ilen));
> >
> >                         i += ilen;
> > @@ -968,10 +976,15 @@ static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
> >
> >                         /* Just print remaining items at once and break */
> >                         DBG("\t%s", item2string(itemstr, &value[i], vlen - i));
> > -                       break;
> > +                       return;
> >                 }
> >         }
> >
> > +       if (collection_depth != 0) {
> > +               error("Report Map error: unbalanced collection");
> > +               return;
> > +       }
> > +
> >         /* create uHID device */
> >         memset(&ev, 0, sizeof(ev));
> >         ev.type = UHID_CREATE;
> > @@ -1365,7 +1378,9 @@ static void foreach_hog_chrc(struct gatt_db_attribute *attr, void *user_data)
> >                          * UHID to optimize reconnection.
> >                          */
> >                         uhid_create(hog, report_map.value, report_map.length);
> > -               } else {
> > +               }
> > +
> > +               if (!hog->uhid_created) {
> >                         read_char(hog, hog->attrib, value_handle,
> >                                                 report_map_read_cb, hog);
> >                 }
> > --
> > 2.29.2
> >
>
>
> --
> Luiz Augusto von Dentz
Sonny Sasaka Jan. 25, 2021, 7:35 p.m. UTC | #3
Hi Luiz,

I have been trying to reproduce the issue again but it turns out to be
very rare. Let's defer this patch until I can get a clear log of what
is happening and why we get the corrupted cache.



On Thu, Jan 21, 2021 at 5:24 PM Sonny Sasaka <sonnysasaka@chromium.org> wrote:
>
> Hi Luiz,
>
> On Thu, Jan 21, 2021 at 4:37 PM Luiz Augusto von Dentz
> <luiz.dentz@gmail.com> wrote:
> >
> > Hi Sonny,
> >
> > On Thu, Jan 21, 2021 at 4:18 PM Sonny Sasaka <sonnysasaka@chromium.org> wrote:
> > >
> > > The report map in the cache could be dirty, for example when reading a
> > > report map from peer was cancelled, we should be able to detect it and
> > > not try to create UHID. Instead we will read it again from the peer.
> >
> > Don't we clean the cache if it had failed? Or you meant to say the
> > read long procedure was not complete so we got just part of the report
> > map?
> Looks like this is the case. It happened to me once when I cancel
> reconnection (trigger pairing mode during reconnection) from the
> keyboard side. It's hard to confirm since I have to get the timing
> right.
>
> > In that case we should have failed
> I agree. However it seems that the code already tries to fail by
> looking at the status inside report_map_read_cb, but somehow it still
> gets through. It could be the keyboard bug that we have to detect
> anyway?
>
> > also if we need to protect
> > uhid from malformed report map, which sounds like a kernel bug, then
> > we should at least have it inside bt_uhid instance so we can at least
> > attempt to have some unit testing done with broken report maps.
> >
> > > ---
> > >  profiles/input/hog-lib.c | 21 ++++++++++++++++++---
> > >  1 file changed, 18 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/profiles/input/hog-lib.c b/profiles/input/hog-lib.c
> > > index 089f42826..d6a3bda4d 100644
> > > --- a/profiles/input/hog-lib.c
> > > +++ b/profiles/input/hog-lib.c
> > > @@ -946,7 +946,7 @@ static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
> > >         struct uhid_event ev;
> > >         ssize_t vlen = report_map_len;
> > >         char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */
> > > -       int i, err;
> > > +       int i, err, collection_depth = 0;
> > >         GError *gerr = NULL;
> > >
> > >         DBG("Report MAP:");
> > > @@ -960,6 +960,14 @@ static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
> > >                         if (!long_item && (value[i] & 0xfc) == 0x84)
> > >                                 hog->has_report_id = TRUE;
> > >
> > > +                       // Start Collection
> > > +                       if (value[i] == 0xa1)
> > > +                               collection_depth++;
> > > +
> > > +                       // End Collection
> > > +                       if (value[i] == 0xc0)
> > > +                               collection_depth--;
> > > +
> > >                         DBG("\t%s", item2string(itemstr, &value[i], ilen));
> > >
> > >                         i += ilen;
> > > @@ -968,10 +976,15 @@ static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
> > >
> > >                         /* Just print remaining items at once and break */
> > >                         DBG("\t%s", item2string(itemstr, &value[i], vlen - i));
> > > -                       break;
> > > +                       return;
> > >                 }
> > >         }
> > >
> > > +       if (collection_depth != 0) {
> > > +               error("Report Map error: unbalanced collection");
> > > +               return;
> > > +       }
> > > +
> > >         /* create uHID device */
> > >         memset(&ev, 0, sizeof(ev));
> > >         ev.type = UHID_CREATE;
> > > @@ -1365,7 +1378,9 @@ static void foreach_hog_chrc(struct gatt_db_attribute *attr, void *user_data)
> > >                          * UHID to optimize reconnection.
> > >                          */
> > >                         uhid_create(hog, report_map.value, report_map.length);
> > > -               } else {
> > > +               }
> > > +
> > > +               if (!hog->uhid_created) {
> > >                         read_char(hog, hog->attrib, value_handle,
> > >                                                 report_map_read_cb, hog);
> > >                 }
> > > --
> > > 2.29.2
> > >
> >
> >
> > --
> > Luiz Augusto von Dentz
Luiz Augusto von Dentz Jan. 25, 2021, 9:50 p.m. UTC | #4
Hi Sonny,

On Mon, Jan 25, 2021 at 11:36 AM Sonny Sasaka <sonnysasaka@chromium.org> wrote:
>
> Hi Luiz,
>
> I have been trying to reproduce the issue again but it turns out to be
> very rare. Let's defer this patch until I can get a clear log of what
> is happening and why we get the corrupted cache.

Ok, let me update in the pw, if you see this again let me know.

>
>
> On Thu, Jan 21, 2021 at 5:24 PM Sonny Sasaka <sonnysasaka@chromium.org> wrote:
> >
> > Hi Luiz,
> >
> > On Thu, Jan 21, 2021 at 4:37 PM Luiz Augusto von Dentz
> > <luiz.dentz@gmail.com> wrote:
> > >
> > > Hi Sonny,
> > >
> > > On Thu, Jan 21, 2021 at 4:18 PM Sonny Sasaka <sonnysasaka@chromium.org> wrote:
> > > >
> > > > The report map in the cache could be dirty, for example when reading a
> > > > report map from peer was cancelled, we should be able to detect it and
> > > > not try to create UHID. Instead we will read it again from the peer.
> > >
> > > Don't we clean the cache if it had failed? Or you meant to say the
> > > read long procedure was not complete so we got just part of the report
> > > map?
> > Looks like this is the case. It happened to me once when I cancel
> > reconnection (trigger pairing mode during reconnection) from the
> > keyboard side. It's hard to confirm since I have to get the timing
> > right.
> >
> > > In that case we should have failed
> > I agree. However it seems that the code already tries to fail by
> > looking at the status inside report_map_read_cb, but somehow it still
> > gets through. It could be the keyboard bug that we have to detect
> > anyway?
> >
> > > also if we need to protect
> > > uhid from malformed report map, which sounds like a kernel bug, then
> > > we should at least have it inside bt_uhid instance so we can at least
> > > attempt to have some unit testing done with broken report maps.
> > >
> > > > ---
> > > >  profiles/input/hog-lib.c | 21 ++++++++++++++++++---
> > > >  1 file changed, 18 insertions(+), 3 deletions(-)
> > > >
> > > > diff --git a/profiles/input/hog-lib.c b/profiles/input/hog-lib.c
> > > > index 089f42826..d6a3bda4d 100644
> > > > --- a/profiles/input/hog-lib.c
> > > > +++ b/profiles/input/hog-lib.c
> > > > @@ -946,7 +946,7 @@ static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
> > > >         struct uhid_event ev;
> > > >         ssize_t vlen = report_map_len;
> > > >         char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */
> > > > -       int i, err;
> > > > +       int i, err, collection_depth = 0;
> > > >         GError *gerr = NULL;
> > > >
> > > >         DBG("Report MAP:");
> > > > @@ -960,6 +960,14 @@ static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
> > > >                         if (!long_item && (value[i] & 0xfc) == 0x84)
> > > >                                 hog->has_report_id = TRUE;
> > > >
> > > > +                       // Start Collection
> > > > +                       if (value[i] == 0xa1)
> > > > +                               collection_depth++;
> > > > +
> > > > +                       // End Collection
> > > > +                       if (value[i] == 0xc0)
> > > > +                               collection_depth--;
> > > > +
> > > >                         DBG("\t%s", item2string(itemstr, &value[i], ilen));
> > > >
> > > >                         i += ilen;
> > > > @@ -968,10 +976,15 @@ static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
> > > >
> > > >                         /* Just print remaining items at once and break */
> > > >                         DBG("\t%s", item2string(itemstr, &value[i], vlen - i));
> > > > -                       break;
> > > > +                       return;
> > > >                 }
> > > >         }
> > > >
> > > > +       if (collection_depth != 0) {
> > > > +               error("Report Map error: unbalanced collection");
> > > > +               return;
> > > > +       }
> > > > +
> > > >         /* create uHID device */
> > > >         memset(&ev, 0, sizeof(ev));
> > > >         ev.type = UHID_CREATE;
> > > > @@ -1365,7 +1378,9 @@ static void foreach_hog_chrc(struct gatt_db_attribute *attr, void *user_data)
> > > >                          * UHID to optimize reconnection.
> > > >                          */
> > > >                         uhid_create(hog, report_map.value, report_map.length);
> > > > -               } else {
> > > > +               }
> > > > +
> > > > +               if (!hog->uhid_created) {
> > > >                         read_char(hog, hog->attrib, value_handle,
> > > >                                                 report_map_read_cb, hog);
> > > >                 }
> > > > --
> > > > 2.29.2
> > > >
> > >
> > >
> > > --
> > > Luiz Augusto von Dentz
diff mbox series

Patch

diff --git a/profiles/input/hog-lib.c b/profiles/input/hog-lib.c
index 089f42826..d6a3bda4d 100644
--- a/profiles/input/hog-lib.c
+++ b/profiles/input/hog-lib.c
@@ -946,7 +946,7 @@  static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
 	struct uhid_event ev;
 	ssize_t vlen = report_map_len;
 	char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */
-	int i, err;
+	int i, err, collection_depth = 0;
 	GError *gerr = NULL;
 
 	DBG("Report MAP:");
@@ -960,6 +960,14 @@  static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
 			if (!long_item && (value[i] & 0xfc) == 0x84)
 				hog->has_report_id = TRUE;
 
+			// Start Collection
+			if (value[i] == 0xa1)
+				collection_depth++;
+
+			// End Collection
+			if (value[i] == 0xc0)
+				collection_depth--;
+
 			DBG("\t%s", item2string(itemstr, &value[i], ilen));
 
 			i += ilen;
@@ -968,10 +976,15 @@  static void uhid_create(struct bt_hog *hog, uint8_t *report_map,
 
 			/* Just print remaining items at once and break */
 			DBG("\t%s", item2string(itemstr, &value[i], vlen - i));
-			break;
+			return;
 		}
 	}
 
+	if (collection_depth != 0) {
+		error("Report Map error: unbalanced collection");
+		return;
+	}
+
 	/* create uHID device */
 	memset(&ev, 0, sizeof(ev));
 	ev.type = UHID_CREATE;
@@ -1365,7 +1378,9 @@  static void foreach_hog_chrc(struct gatt_db_attribute *attr, void *user_data)
 			 * UHID to optimize reconnection.
 			 */
 			uhid_create(hog, report_map.value, report_map.length);
-		} else {
+		}
+
+		if (!hog->uhid_created) {
 			read_char(hog, hog->attrib, value_handle,
 						report_map_read_cb, hog);
 		}