about summary refs log tree commit diff
path: root/stdlib/tst-getrandom.c
blob: 608fc4746eed3ed69b53bd532c1db8dfa4316bad (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/* Tests for the getentropy, getrandom functions.
   Copyright (C) 2016-2022 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/random.h>

/* Set to true if any errors are encountered.  */
static bool errors;

/* Test getrandom with a single buffer length.  NB: The passed-in
   buffer must have room for four extra bytes after the specified
   length, which are used to test that getrandom leaves those bytes
   unchanged.  */
static void
test_length (char *buffer, size_t length, unsigned int flags)
{
  memset (buffer, 0, length);
  strcpy (buffer + length, "123");
  ssize_t ret = getrandom (buffer, length, flags);
  if (ret < 0)
    {
      /* EAGAIN is an expected error with GRND_RANDOM and
         GRND_NONBLOCK.  */
      if ((flags & GRND_RANDOM)
          && (flags & GRND_NONBLOCK)
          && errno == EAGAIN)
        return;
      printf ("error: getrandom (%zu, 0x%x): %m\n", length, flags);
      errors = true;
      return;
    }
 if (ret != length)
    {
      if (flags & GRND_RANDOM)
        {
          if (ret == 0 || ret > length)
            {
              printf ("error: getrandom (%zu, 0x%x) returned %zd\n",
                      length, flags, ret);
              errors = true;
            }
        }
      else
        {
          printf ("error: getrandom (%zu, 0x%x) returned %zd\n",
                  length, flags, ret);
          errors = true;
        }
    }
  if (length >= 7)
    {
      /* One spurious test failure in 2**56 is sufficiently
         unlikely.  */
      int non_null = 0;
      for (int i = 0; i < length; ++i)
        non_null += buffer[i] != 0;
      if (non_null == 0)
        {
          printf ("error: getrandom (%zu, 0x%x) returned all-zero bytes\n",
                  length, flags);
          errors = true;
        }
    }
  if (memcmp (buffer + length, "123", 4) != 0)
    {
      printf ("error: getrandom (%zu, 0x%x) wrote spurious bytes\n",
              length, flags);
      errors = true;
    }
}

/* Call getrandom repeatedly to fill the buffer.  */
static bool
getrandom_full (char *buffer, size_t length, unsigned int flags)
{
  char *end = buffer + length;
  while (buffer < end)
    {
      ssize_t ret = getrandom (buffer, end - buffer, flags);
      if (ret < 0)
        {
          printf ("error: getrandom (%zu, 0x%x): %m\n", length, flags);
          errors = true;
          return false;
        }
      buffer += ret;
    }

  return true;
}

static void
test_flags (unsigned int flags)
{
  /* Test various lengths, but only for !GRND_RANDOM, to conserve
     entropy.  */
  {
    enum { max_length = 300 };
    char buffer[max_length + 4];
    if (flags & GRND_RANDOM)
      test_length (buffer, 0, flags);
    else
      {
        for (int length = 0; length <= 9; ++length)
          test_length (buffer, length, flags);
        test_length (buffer, 16, flags);
        test_length (buffer, max_length, flags);
      }
  }

  /* Test that getrandom returns different data.  */
  if (!(flags & GRND_NONBLOCK))
    {
      char buffer1[8];
      memset (buffer1, 0, sizeof (buffer1));

      char buffer2[8];
      memset (buffer2, 0, sizeof (buffer2));

      if (getrandom_full (buffer1, sizeof (buffer1), flags)
          && getrandom_full (buffer2, sizeof (buffer2), flags))
        {
          /* The probability that these two 8-byte buffers are equal
             is very small (assuming that two subsequent calls to
             getrandom result are independent, uniformly distributed
             random variables).  */
          if (memcmp (buffer1, buffer2, sizeof (buffer1)) == 0)
            {
              printf ("error: getrandom returns constant value\n");
              errors = true;
            }
        }
    }
}

static void
test_getentropy (void)
{
  char buf[16];
  memset (buf, '@', sizeof (buf));
  if (getentropy (buf, 0) != 0)
    {
      printf ("error: getentropy zero length: %m\n");
      errors = true;
      return;
    }
  for (size_t i = 0; i < sizeof (buf); ++i)
    if (buf[i] != '@')
      {
        printf ("error: getentropy modified zero-length buffer\n");
        errors = true;
        return;
      }

  if (getentropy (buf, sizeof (buf)) != 0)
    {
      printf ("error: getentropy buf: %m\n");
      errors = true;
      return;
    }

  char buf2[256];
  _Static_assert (sizeof (buf) < sizeof (buf2), "buf and buf2 compatible");
  memset (buf2, '@', sizeof (buf2));
  if (getentropy (buf2, sizeof (buf)) != 0)
    {
      printf ("error: getentropy buf2: %m\n");
      errors = true;
      return;
    }

  /* The probability that these two buffers are equal is very
     small. */
  if (memcmp (buf, buf2, sizeof (buf) == 0))
    {
      printf ("error: getentropy appears to return constant bytes\n");
      errors = true;
      return;
    }

  for (size_t i = sizeof (buf); i < sizeof (buf2); ++i)
    if (buf2[i] != '@')
      {
        printf ("error: getentropy wrote beyond the end of the buffer\n");
        errors = true;
        return;
      }

  char buf3[257];
  if (getentropy (buf3, sizeof (buf3)) == 0)
    {
      printf ("error: getentropy successful for 257 byte buffer\n");
      errors = true;
      return;
    }
  if (errno != EIO)
    {
      printf ("error: getentropy wrong error for 257 byte buffer: %m\n");
      errors = true;
      return;
    }
}

static int
do_test (void)
{
  /* Check if getrandom is not supported by this system.  */
  if (getrandom (NULL, 0, 0) == -1 && errno == ENOSYS)
    return 77;

  for (int use_random = 0; use_random < 2; ++use_random)
    for (int use_nonblock = 0; use_nonblock < 2; ++use_nonblock)
      {
        unsigned int flags = 0;
        if (use_random)
          flags |= GRND_RANDOM;
        if (use_nonblock)
          flags |= GRND_NONBLOCK;
        test_flags (flags);
      }

  test_getentropy ();

  return errors;
}

#include <support/test-driver.c>