bitvec.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // SPDX-License-Identifier: GPL-2.0 or GPL-3.0
  2. // Copyright © 2018-2019 Ariadne Devos
  3. /* sHT -- test the sHT_bit_test function */
  4. #include <sHT/bitvec.h>
  5. #include <sHT/index.h>
  6. #include <sHT/test.h>
  7. #include <limits.h>
  8. #include <stddef.h>
  9. #include <stdio.h>
  10. #include <stdint.h>
  11. #include <string.h>
  12. #include <sys/mman.h>
  13. #include <unistd.h>
  14. /* This tests that:
  15. * the bit vector may be unaligned,
  16. * the bit order is correct.
  17. * the vector may be longer than 64-bits
  18. * no memory before or after the bitvector is accessed
  19. (on virtual-memory systems)
  20. * the bitvector may be read-only
  21. (on virtual-memory systems).
  22. The correctness of the bit vector is verified with a typical
  23. reference implementation, and a trivial table lookup. -- s^2
  24. does not always use the reference implementation, on x86, it
  25. uses a special instruction.
  26. Very hypothetical TODO: this test could be speculatively unsafe. */
  27. /* A random bitvector, expanded to @var{reference}. */
  28. const uint8_t compact[] = {
  29. 0b00110101,
  30. 0b11010101,
  31. 0b01101110,
  32. 0b10110110,
  33. 0b11000101,
  34. 0b00100111,
  35. 0b00000010,
  36. 0b10011001,
  37. 0b00000000,
  38. 0b10001111,
  39. 0b10100001,
  40. 0b11001001,
  41. 0b00111001,
  42. 0b11100001,
  43. 0b10011110,
  44. 0b10101010,
  45. 0b00110101,
  46. };
  47. static const _Bool reference[] = {
  48. 1, 0, 1, 0, 1, 1, 0, 0,
  49. 1, 0, 1, 0, 1, 0, 1, 1,
  50. 0, 1, 1, 1, 0, 1, 1, 0,
  51. 0, 1, 1, 0, 1, 1, 0, 1,
  52. 1, 0, 1, 0, 0, 0, 1, 1,
  53. 1, 1, 1, 0, 0, 1, 0, 0,
  54. 0, 1, 0, 0, 0, 0, 0, 0,
  55. 1, 0, 0, 1, 1, 0, 0, 1,
  56. 0, 0, 0, 0, 0, 0, 0, 0,
  57. 1, 1, 1, 1, 0, 0, 0, 1,
  58. 1, 0, 0, 0, 0, 1, 0, 1,
  59. 1, 0, 0, 1, 0, 0, 1, 1,
  60. 1, 0, 0, 1, 1, 1, 0, 0,
  61. 1, 0, 0, 0, 0, 1, 1, 1,
  62. 0, 1, 1, 1, 1, 0, 0, 1,
  63. 0, 1, 0, 1, 0, 1, 0, 1,
  64. 1, 0, 1, 0, 1, 1, 0, 0,
  65. };
  66. #define reference_test(i, vec) ((_Bool) ((vec)[(i) / 8] & (1 << ((i) % 8))))
  67. int
  68. main(void)
  69. {
  70. /* Make three different tables:
  71. one at the end of a page,
  72. one the beginning of a page,
  73. and the original table.
  74. Allocate three pages, write the bitvector,
  75. then disallow access to the start and the end
  76. and only allow read access to the middle.
  77. If the bit vector code then tries to trespass the boundary,
  78. or write to the bitvector, a segfault results.
  79. x86 has bit test instructions that change the bit vector,
  80. so it makes sense to require read-only access. */
  81. unsigned long page_size;
  82. #ifdef PAGE_SIZE
  83. page_size = PAGE_SIZE;
  84. #else
  85. page_size = sysconf(_SC_PAGESIZE);
  86. if (page_size == (unsigned long) (long) -1) {
  87. perror("not posix: _SC_PAGESIZE");
  88. return 2;
  89. }
  90. #endif
  91. if (2 * sizeof(compact) > page_size) {
  92. fprintf(stderr, "%s", "very small pages\n");
  93. return 2;
  94. }
  95. uint8_t *start = mmap(NULL, 3 * page_size, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  96. if (sHT_eq_pointer(start, MAP_FAILED)) {
  97. perror("allocate memory");
  98. return 2;
  99. }
  100. memcpy(start + page_size, compact, sizeof(compact));
  101. memcpy(start + 2 * page_size - sizeof(compact), compact, sizeof(compact));
  102. if (mprotect(start, page_size, PROT_NONE))
  103. goto prot_change_error;
  104. if (mprotect(start + page_size, page_size, PROT_READ))
  105. goto prot_change_error;
  106. if (mprotect(start + 2 * page_size, page_size, PROT_NONE)) {
  107. prot_change_error:
  108. perror("change protections");
  109. return 2;
  110. }
  111. const uint8_t *vec1 = compact;
  112. const uint8_t *vec2 = start + page_size;
  113. const uint8_t *vec3 = start + 2 * page_size - sizeof(compact);
  114. /* Compare two implementations on the three bitvectors
  115. with the reference table. */
  116. size_t i;
  117. sHT_index_iterate(i, 8 * sizeof(compact)) {
  118. int n = 0;
  119. n += reference[i];
  120. n += reference_test(i, vec1);
  121. n += sHT_bit_test(vec2, i);
  122. n += sHT_bit_test(vec3, i);
  123. if ((n != 4) && (n != 0)) {
  124. if (printf("FAIL: bitvec (%zu)\n", i) < 0)
  125. return 2;
  126. return 1;
  127. }
  128. }
  129. if (printf("PASS: bitvec\n", i) < 0)
  130. return 2;
  131. return 0;
  132. }