[glib: 1/2] GBytes: add range-checked pointer getter
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib: 1/2] GBytes: add range-checked pointer getter
- Date: Tue, 15 Jun 2021 12:01:13 +0000 (UTC)
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]