nss: Add ERANGE testing to tst-nss-test4 (bug 33361)

This adds testing for the fix added in commit:
0fceed2545
"nss: Group merge does not react to ERANGE during merge (bug 33361)"

The in-use group size is increased large enough to trigger ERANGE
for initial buffers and cause a retry.  The actualy size is
approximately twice that required to trigger the defect, though
any size larger than NSS_BUFLEN_GROUP triggers the defect.

Without the fix the group is not merged and the failure is detected,
but with the fix the ERANGE error is handled, buffers are enlarged
and subsequently correctly merged.

Tested with a/b testing before and after patching.
Tested on x86_64 with no regression.

Co-authored-by: Patsy Griffin <patsy@redhat.com>
Reviewed-by: DJ Delorie <dj@redhat.com>
This commit is contained in:
Carlos O'Donell 2025-11-07 09:46:10 -05:00
parent 259adb087d
commit ea8e2b96d8
1 changed files with 94 additions and 37 deletions

View File

@ -16,11 +16,16 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <assert.h>
#include <nss.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <array_length.h>
/* For NSS_BUFLEN_GROUP define. */
#include "nss/grp.h"
#include <support/test-driver.h>
#include <support/support.h>
#include "nss_test.h"
@ -32,14 +37,10 @@ static const char *group_1[] = {
"foo", "bar", NULL
};
static const char *group_2[] = {
"foo", "dick", "harry", NULL
};
/* Enough entries to exceed NSS_BUFLEN_GROUP and trigger ERANGE. */
static char *group_2[256];
/* Note that deduplication is NOT supposed to happen. */
static const char *merge_1[] = {
"foo", "bar", "foo", "dick", "harry", NULL
};
static char *merge_1[array_length(group_1) + array_length (group_2) - 1];
static const char *group_4[] = {
"fred", "wilma", NULL
@ -59,6 +60,14 @@ static struct group group_table_data2[] = {
GRP_LAST ()
};
/* In order to trigger ERANGE checking the minimum size of
group_table_data2 should exceed NSS_BUFLEN_GROUP which is used
internally by getgrgid. We use 8 bytes per group_2 string as
a lower bound. */
_Static_assert (sizeof (group_table_data2) + array_length (group_2) * 8
>= NSS_BUFLEN_GROUP,
"test group table size should exceed NSS_BUFLEN_GROUP");
/* This is the data we compare against. */
static struct group group_table[] = {
GRP_N(1, "name1", merge_1),
@ -83,42 +92,90 @@ static int
do_test (void)
{
int retval = 0;
int i;
int i, member_cnt;
struct group *g = NULL;
uintptr_t align_mask;
uintptr_t align_mem_mask;
__nss_configure_lookup ("group", "test1 [SUCCESS=merge] test2");
/* At least 3 service modules are needed to reproduce BZ#33361. */
__nss_configure_lookup ("group", "test1 [SUCCESS=merge] test2 files");
align_mask = __alignof__ (struct group *) - 1;
setgrent ();
for (i = 0; group_table[i].gr_gid; ++i)
/* Test increasing sizes of group_2 to see if we fail, starting with
member_cnt == 1 to ensure we always check for no de-duplication
e.g. { "foo", NULL } */
for (member_cnt = 1; member_cnt < array_length (group_2); member_cnt++)
{
g = getgrgid (group_table[i].gr_gid);
if (g)
{
retval += compare_groups (i, g, & group_table[i]);
if ((uintptr_t)g & align_mask)
{
printf("FAIL: [%d] unaligned group %p\n", i, g);
++retval;
}
if ((uintptr_t)(g->gr_mem) & align_mask)
{
printf("FAIL: [%d] unaligned member list %p\n", i, g->gr_mem);
++retval;
}
}
else
{
printf ("FAIL: [%d] group %u.%s not found\n", i,
group_table[i].gr_gid, group_table[i].gr_name);
++retval;
}
}
verbose_printf ("Outer loop - member_cnt is %d\n", member_cnt);
endgrent ();
/* Initialize group_2 */
for (i = 0; i < member_cnt; i++)
{
/* Note that deduplication is NOT supposed to happen. */
if (i == 0)
group_2[i] = xstrdup ("foo");
else
group_2[i] = xasprintf ("foobar%d", i);
}
group_2[member_cnt] = NULL;
/* Create the merged list to verify against */
/* Copy group_1 to the merge list (excluding NULL) */
for (i = 0; i < array_length (group_1) - 1; i++)
{
merge_1[i] = xasprintf ("%s", group_1[i]);
verbose_printf ("MERGED LIST of [%d] is %s\n", i, merge_1[i]);
}
/* Add group_2 to the merge list */
int group2_index = 0;
for (i = array_length (group_1) - 1;
i < array_length (group_1) - 1 + member_cnt; i++)
{
merge_1[i] = xasprintf ("%s", group_2[group2_index++]);
verbose_printf ("MERGED LIST of [%d] is %s\n", i, merge_1[i]);
}
merge_1[array_length(group_1) - 1 + member_cnt]= NULL;
align_mask = __alignof__ (struct group) - 1;
align_mem_mask = __alignof__ (char *) - 1;
setgrent ();
for (i = 0; group_table[i].gr_gid; ++i)
{
g = getgrgid (group_table[i].gr_gid);
if (g)
{
retval += compare_groups (i, g, & group_table[i]);
if ((uintptr_t)g & align_mask)
{
printf ("FAIL: [%d] unaligned group %p\n", i, g);
++retval;
}
if ((uintptr_t)(g->gr_mem) & align_mem_mask)
{
printf ("FAIL: [%d] unaligned member list %p\n",
i, g->gr_mem);
++retval;
}
}
else
{
printf ("FAIL: [%d] group %u.%s not found\n", i,
group_table[i].gr_gid, group_table[i].gr_name);
++retval;
}
}
endgrent ();
/* Free malloc'd array members (including the NULL) */
for (i = 0; i < member_cnt; i++)
free (group_2[i]);
for (i = 0; i < array_length (group_1) + member_cnt; i++)
free (merge_1[i]);
}
#define EXPECTED 0
if (retval == EXPECTED)