mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
GBytes: add range-checked pointer getter
Updated and improved by Nitin Wartkar. Fixes: #1098
This commit is contained in:
parent
9d2d99efe7
commit
e3452ea01f
@ -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
|
||||
|
@ -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;
|
||||
}
|
@ -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__ */
|
||||
|
@ -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 ();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user