[glib: 1/2] GBytes: add range-checked pointer getter




commit e3452ea01fe8acc74ddedd57ad9e2f4987366d63
Author: Nitin Wartkar <nitinwartkar58 gmail com>
Date:   Tue Jun 15 12:01:11 2021 +0000

    GBytes: add range-checked pointer getter
    
    Updated and improved by Nitin Wartkar.
    
    Fixes: #1098

 docs/reference/glib/glib-sections.txt |  1 +
 glib/gbytes.c                         | 72 +++++++++++++++++++++++++++++++++++
 glib/gbytes.h                         |  7 ++++
 glib/tests/bytes.c                    | 33 ++++++++++++++++
 4 files changed, 113 insertions(+)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index 74f7b2149..765c48aa5 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -3028,6 +3028,7 @@ g_bytes_new_static
 g_bytes_new_with_free_func
 g_bytes_new_from_bytes
 g_bytes_get_data
+g_bytes_get_region
 g_bytes_get_size
 g_bytes_hash
 g_bytes_equal
diff --git a/glib/gbytes.c b/glib/gbytes.c
index aaadf451b..a6ca0e300 100644
--- a/glib/gbytes.c
+++ b/glib/gbytes.c
@@ -538,3 +538,75 @@ g_bytes_unref_to_array (GBytes *bytes)
   data = g_bytes_unref_to_data (bytes, &size);
   return g_byte_array_new_take (data, size);
 }
+
+/**
+ * g_bytes_get_region:
+ * @bytes: a #GBytes
+ * @element_size: a non-zero element size
+ * @offset: an offset to the start of the region within the @bytes
+ * @n_elements: the number of elements in the region
+ *
+ * Gets a pointer to a region in @bytes.
+ *
+ * The region starts at @offset many bytes from the start of the data
+ * and contains @n_elements many elements of @element_size size.
+ *
+ * @n_elements may be zero, but @element_size must always be non-zero.
+ * Ideally, @element_size is a static constant (eg: sizeof a struct).
+ *
+ * This function does careful bounds checking (including checking for
+ * arithmetic overflows) and returns a non-%NULL pointer if the
+ * specified region lies entirely within the @bytes. If the region is
+ * in some way out of range, or if an overflow has occurred, then %NULL
+ * is returned.
+ *
+ * Note: it is possible to have a valid zero-size region. In this case,
+ * the returned pointer will be equal to the base pointer of the data of
+ * @bytes, plus @offset.  This will be non-%NULL except for the case
+ * where @bytes itself was a zero-sized region.  Since it is unlikely
+ * that you will be using this function to check for a zero-sized region
+ * in a zero-sized @bytes, %NULL effectively always means "error".
+ *
+ * Returns: (nullable): the requested region, or %NULL in case of an error
+ *
+ * Since: 2.70
+ */
+gconstpointer
+g_bytes_get_region (GBytes *bytes,
+                    gsize   element_size,
+                    gsize   offset,
+                    gsize   n_elements)
+{
+  gsize total_size;
+  gsize end_offset;
+
+  g_return_val_if_fail (element_size > 0, NULL);
+
+  /* No other assertion checks here.  If something is wrong then we will
+   * simply crash (via NULL dereference or divide-by-zero).
+   */
+
+  if (!g_size_checked_mul (&total_size, element_size, n_elements))
+    return NULL;
+
+  if (!g_size_checked_add (&end_offset, offset, total_size))
+    return NULL;
+
+  /* We now have:
+   *
+   *   0 <= offset <= end_offset
+   *
+   * So we need only check that end_offset is within the range of the
+   * size of @bytes and we're good to go.
+   */
+
+  if (end_offset > bytes->size)
+    return NULL;
+
+  /* We now have:
+   *
+   *   0 <= offset <= end_offset <= bytes->size
+   */
+
+  return ((guchar *) bytes->data) + offset;
+}
\ No newline at end of file
diff --git a/glib/gbytes.h b/glib/gbytes.h
index 0bb1517b9..37cad861d 100644
--- a/glib/gbytes.h
+++ b/glib/gbytes.h
@@ -85,6 +85,13 @@ GLIB_AVAILABLE_IN_ALL
 gint            g_bytes_compare                 (gconstpointer   bytes1,
                                                  gconstpointer   bytes2);
 
+GLIB_AVAILABLE_IN_2_70
+gconstpointer   g_bytes_get_region              (GBytes         *bytes,
+                                                 gsize           element_size,
+                                                 gsize           offset,
+                                                 gsize           n_elements);
+
+
 G_END_DECLS
 
 #endif /* __G_BYTES_H__ */
diff --git a/glib/tests/bytes.c b/glib/tests/bytes.c
index fc02a211c..e4be70aad 100644
--- a/glib/tests/bytes.c
+++ b/glib/tests/bytes.c
@@ -418,6 +418,38 @@ test_null (void)
   g_assert (size == 0);
 }
 
+static void
+test_get_region (void)
+{
+  GBytes *bytes;
+
+  bytes = g_bytes_new_static (NYAN, N_NYAN);
+
+  /* simple valid gets at the start */
+  g_assert_true (g_bytes_get_region (bytes, 1, 0, 1) == NYAN);
+  g_assert_true (g_bytes_get_region (bytes, 1, 0, N_NYAN) == NYAN);
+
+  /* an invalid get because the range is too wide */
+  g_assert_true (g_bytes_get_region (bytes, 1, 0, N_NYAN + 1) == NULL);
+
+  /* an valid get, but of a zero-byte range at the end */
+  g_assert_true (g_bytes_get_region (bytes, 1, N_NYAN, 0) == NYAN + N_NYAN);
+
+  /* not a valid get because it overlap ones byte */
+  g_assert_true (g_bytes_get_region (bytes, 1, N_NYAN, 1) == NULL);
+
+  /* let's try some multiplication overflow now */
+  g_assert_true (g_bytes_get_region (bytes, 32, 0, G_MAXSIZE / 32 + 1) == NULL);
+  g_assert_true (g_bytes_get_region (bytes, G_MAXSIZE / 32 + 1, 0, 32) == NULL);
+
+  /* and some addition overflow */
+  g_assert_true (g_bytes_get_region (bytes, 1, G_MAXSIZE, -G_MAXSIZE) == NULL);
+  g_assert_true (g_bytes_get_region (bytes, 1, G_MAXSSIZE, ((gsize) G_MAXSSIZE) + 1) == NULL);
+  g_assert_true (g_bytes_get_region (bytes, 1, G_MAXSIZE, 1) == NULL);
+
+  g_bytes_unref (bytes);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -441,6 +473,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/bytes/to-array/two-refs", test_to_array_two_refs);
   g_test_add_func ("/bytes/to-array/non-malloc", test_to_array_non_malloc);
   g_test_add_func ("/bytes/null", test_null);
+  g_test_add_func ("/bytes/get-region", test_get_region);
 
   return g_test_run ();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]