History

Add linenumber filename support for bounds checking.

herman ten brugge committed 6 years ago

Commit 87639aa1 parent 56e70bf3 files · +154 −124Browse files

Unified · Split

3 files
  1. i386-gen.c +25 −18
  2. lib/bcheck.c +91 −85
  3. x86_64-gen.c +38 −21
i386-gen.c+25−18
@@ -1130,29 +1130,36 @@ ST_FUNC void gen_bounded_ptr_add(void) · 1129 unmodified lines
1130{1130{
1131 /* save all temporary registers */1131 /* save all temporary registers */
1132 save_regs(0);1132 save_regs(0);
1133 /* trick to get line/file_name in code */
1134 o(0xb8);
1135 gen_le32 (file->line_num);
1136 {
1137 int i;
1138 int len;
1139 char *cp = file->filename;
1140 while (*cp) cp++;
1141 while (cp != file->filename && cp[-1] != '/') cp--;
1142 len = strlen (cp);
1143 while (len > 0) {
1144 memcpy (&i, cp, 4);
1145 o(0xb8);
1146 gen_le32 (i);
1147 cp += 4;
1148 len -= 4;
1149 }
1150 }
1151 /* prepare fast i386 function call (args in eax and edx) */1133 /* prepare fast i386 function call (args in eax and edx) */
1152 gv2(RC_EAX, RC_EDX);1134 gv2(RC_EAX, RC_EDX);
1153 vtop -= 2;1135 vtop -= 2;
1136 /* add line, filename */
1137 {
1138 static addr_t offset;
1139 static char last_filename[1024];
1140 Sym *sym_data;
1141
1142 if (strcmp (last_filename, file->filename) != 0) {
1143 void *ptr;
1144 int len = strlen (file->filename) + 1;
1145
1146 offset = data_section->data_offset;
1147 ptr = section_ptr_add(data_section, len);
1148 memcpy (ptr, file->filename, len);
1149 memcpy (last_filename, file->filename, len);
1150 }
1151 o(0xb9); /* mov $xx,%ecx */
1152 gen_le32 (0);
1153 sym_data = get_sym_ref(&char_pointer_type, data_section,
1154 offset, data_section->data_offset);
1155 greloca(cur_text_section, sym_data, ind - 4, R_386_32, 0);
1156 o(0x51); /* push %ecx */
1157 }
1158 o(0xb9); /* mov $xx,%ecx */
1159 gen_le32 (file->line_num);
1154 /* do a fast function call */1160 /* do a fast function call */
1155 gen_static_call(TOK___bound_ptr_add);1161 gen_static_call(TOK___bound_ptr_add);
1162 o(0x04c483); /* add $4,%esp */
1156 /* returned pointer is in eax */1163 /* returned pointer is in eax */
1157 vtop++;1164 vtop++;
1158 vtop->r = TREG_EAX | VT_BOUNDED;1165 vtop->r = TREG_EAX | VT_BOUNDED;
lib/bcheck.c+91−85
@@ -221,7 +221,8 @@ static void bound_alloc_error(void) · 220 unmodified lines
221 221
222/* return '(p + offset)' for pointer arithmetic (a pointer can reach222/* return '(p + offset)' for pointer arithmetic (a pointer can reach
223 the end of a region in this case */223 the end of a region in this case */
224void * FASTCALL __bound_ptr_add(void *p, size_t offset)224void * FASTCALL __bound_ptr_add(void *p, size_t offset,
225 size_t line, const char *filename)
225{226{
226 size_t addr = (size_t)p;227 size_t addr = (size_t)p;
227 228
@@ -229,8 +230,8 @@ void * FASTCALL __bound_ptr_add(void *p, size_t offset) · 1 unmodified line
229 return p + offset;230 return p + offset;
230 }231 }
231 232
232 dprintf(stderr, "%s %s: %p 0x%x\n",233 dprintf(stderr, "%s %s (%s:%u): %p 0x%x\n",
233 __FILE__, __FUNCTION__, p, (unsigned)offset);234 __FILE__, __FUNCTION__, filename, line, p, (unsigned)offset);
234 235
235 WAIT_SEM ();236 WAIT_SEM ();
236 INCR_COUNT(bound_ptr_add_count);237 INCR_COUNT(bound_ptr_add_count);
@@ -249,8 +250,8 @@ void * FASTCALL __bound_ptr_add(void *p, size_t offset) · 12 unmodified lines
249 if (addr <= tree->size) {250 if (addr <= tree->size) {
250 addr += offset;251 addr += offset;
251 if (tree->is_invalid || addr > tree->size) {252 if (tree->is_invalid || addr > tree->size) {
252 fprintf(stderr,"%s %s: %p is outside of the region\n",253 fprintf(stderr,"%s %s (%s:%u): %p is outside of the region\n",
253 __FILE__, __FUNCTION__, p + offset);254 __FILE__, __FUNCTION__, filename, line, p + offset);
254 if (never_fatal == 0) {255 if (never_fatal == 0) {
255 POST_SEM ();256 POST_SEM ();
256 return INVALID_POINTER; /* return an invalid pointer */257 return INVALID_POINTER; /* return an invalid pointer */
@@ -264,44 +265,45 @@ void * FASTCALL __bound_ptr_add(void *p, size_t offset) · 7 unmodified lines
264 265
265/* return '(p + offset)' for pointer indirection (the resulting must266/* return '(p + offset)' for pointer indirection (the resulting must
266 be strictly inside the region */267 be strictly inside the region */
267#define BOUND_PTR_INDIR(dsize) \268#define BOUND_PTR_INDIR(dsize) \
268void * FASTCALL __bound_ptr_indir ## dsize (void *p, size_t offset) \269void * FASTCALL __bound_ptr_indir ## dsize (void *p, size_t offset, \
269{ \270 size_t line, const char *filename) \
270 size_t addr = (size_t)p; \271{ \
271 \272 size_t addr = (size_t)p; \
272 if (no_checking) { \273 \
273 return p + offset; \274 if (no_checking) { \
274 } \275 return p + offset; \
275 dprintf(stderr, "%s %s: %p 0x%x start\n", \276 } \
276 __FILE__, __FUNCTION__, p, (unsigned)offset); \277 dprintf(stderr, "%s %s (%s:%u): %p 0x%x start\n", \
277 WAIT_SEM (); \278 __FILE__, __FUNCTION__, filename, line, p, (unsigned)offset); \
278 INCR_COUNT(bound_ptr_indir ## dsize ## _count); \279 WAIT_SEM (); \
279 if (tree) { \280 INCR_COUNT(bound_ptr_indir ## dsize ## _count); \
280 addr -= tree->start; \281 if (tree) { \
281 if (addr >= tree->size) { \282 addr -= tree->start; \
282 addr = (size_t)p; \283 if (addr >= tree->size) { \
283 tree = splay (addr, tree); \284 addr = (size_t)p; \
284 addr -= tree->start; \285 tree = splay (addr, tree); \
285 } \286 addr -= tree->start; \
286 if (addr >= tree->size) { \287 } \
287 addr = (size_t)p; \288 if (addr >= tree->size) { \
288 tree = splay_end (addr, tree); \289 addr = (size_t)p; \
289 addr -= tree->start; \290 tree = splay_end (addr, tree); \
290 } \291 addr -= tree->start; \
291 if (addr <= tree->size) { \292 } \
292 addr += offset + dsize; \293 if (addr <= tree->size) { \
293 if (tree->is_invalid || addr > tree->size) { \294 addr += offset + dsize; \
294 fprintf(stderr,"%s %s: %p is outside of the region\n", \295 if (tree->is_invalid || addr > tree->size) { \
295 __FILE__, __FUNCTION__, p + offset); \296 fprintf(stderr,"%s %s (%s:%u): %p is outside of the region\n", \
296 if (never_fatal == 0) { \297 __FILE__, __FUNCTION__, filename, line, p + offset); \
297 POST_SEM (); \298 if (never_fatal == 0) { \
298 return INVALID_POINTER; /* return an invalid pointer */ \299 POST_SEM (); \
299 } \300 return INVALID_POINTER; /* return an invalid pointer */ \
300 } \301 } \
301 } \302 } \
302 } \303 } \
303 POST_SEM (); \304 } \
304 return p + offset; \305 POST_SEM (); \
306 return p + offset; \
305}307}
306 308
307BOUND_PTR_INDIR(1)309BOUND_PTR_INDIR(1)
@@ -540,7 +542,7 @@ void __attribute__((destructor)) __bound_exit(void) · 232 unmodified lines
540 }542 }
541 while (tree) {543 while (tree) {
542 if (print_heap && tree->type != 0) {544 if (print_heap && tree->type != 0) {
543 fprintf (stderr, "%s, %s() %s found size %ld\n",545 fprintf (stderr, "%s, %s() %s found size %lu\n",
544 __FILE__, __FUNCTION__, alloc_type[tree->type],546 __FILE__, __FUNCTION__, alloc_type[tree->type],
545 (unsigned long) tree->size);547 (unsigned long) tree->size);
546 }548 }
@@ -621,7 +623,7 @@ void *__bound_malloc(size_t size, const void *caller) · 74 unmodified lines
621 623
622 if (ptr) {624 if (ptr) {
623 tree = splay_insert ((size_t) ptr, size, tree);625 tree = splay_insert ((size_t) ptr, size, tree);
624 if (tree->start == (size_t) ptr) {626 if (tree && tree->start == (size_t) ptr) {
625 tree->type = TCC_TYPE_MALLOC;627 tree->type = TCC_TYPE_MALLOC;
626 }628 }
627 }629 }
@@ -666,7 +668,7 @@ void *__bound_memalign(size_t size, size_t align, const void *caller) · 38 unmodified lines
666 dprintf(stderr, "%s, %s (%p, 0x%x)\n",668 dprintf(stderr, "%s, %s (%p, 0x%x)\n",
667 __FILE__, __FUNCTION__, ptr, (unsigned)size);669 __FILE__, __FUNCTION__, ptr, (unsigned)size);
668 tree = splay_insert((size_t) ptr, size, tree);670 tree = splay_insert((size_t) ptr, size, tree);
669 if (tree->start == (size_t) ptr) {671 if (tree && tree->start == (size_t) ptr) {
670 tree->type = TCC_TYPE_MEMALIGN;672 tree->type = TCC_TYPE_MEMALIGN;
671 }673 }
672 }674 }
@@ -744,7 +746,7 @@ void *__bound_realloc(void *ptr, size_t size, const void *caller) · 71 unmodified lines
744 INCR_COUNT(bound_realloc_count);746 INCR_COUNT(bound_realloc_count);
745 if (ptr) {747 if (ptr) {
746 tree = splay_insert ((size_t) ptr, size, tree);748 tree = splay_insert ((size_t) ptr, size, tree);
747 if (tree->start == (size_t) ptr) {749 if (tree && tree->start == (size_t) ptr) {
748 tree->type = TCC_TYPE_REALLOC;750 tree->type = TCC_TYPE_REALLOC;
749 }751 }
750 }752 }
@@ -761,7 +763,7 @@ void *__bound_realloc(void *ptr, size_t size, const void *caller) · 10 unmodified lines
761#endif763#endif
762 if (ptr) {764 if (ptr) {
763 tree = splay_insert ((size_t) ptr, size, tree);765 tree = splay_insert ((size_t) ptr, size, tree);
764 if (tree->start == (size_t) ptr) {766 if (tree && tree->start == (size_t) ptr) {
765 tree->type = TCC_TYPE_REALLOC;767 tree->type = TCC_TYPE_REALLOC;
766 }768 }
767 }769 }
@@ -800,7 +802,7 @@ void *__bound_calloc(size_t nmemb, size_t size) · 32 unmodified lines
800 WAIT_SEM ();802 WAIT_SEM ();
801 INCR_COUNT(bound_calloc_count);803 INCR_COUNT(bound_calloc_count);
802 tree = splay_insert ((size_t) ptr, size, tree);804 tree = splay_insert ((size_t) ptr, size, tree);
803 if (tree->start == (size_t) ptr) {805 if (tree && tree->start == (size_t) ptr) {
804 tree->type = TCC_TYPE_CALLOC;806 tree->type = TCC_TYPE_CALLOC;
805 }807 }
806 POST_SEM ();808 POST_SEM ();
@@ -899,13 +901,13 @@ void __bound_new_region(void *p, size_t size) · 92 unmodified lines
899/* some useful checked functions */901/* some useful checked functions */
900 902
901/* check that (p ... p + size - 1) lies inside 'p' region, if any */903/* check that (p ... p + size - 1) lies inside 'p' region, if any */
902static void __bound_check(const void *p, size_t size)904static void __bound_check(const void *p, size_t size, const char *function)
903{905{
904 if (no_checking)906 if (no_checking)
905 return;907 return;
906 if (size == 0)908 if (size == 0)
907 return;909 return;
908 p = __bound_ptr_add((void *)p, size);910 p = __bound_ptr_add((void *)p, size, 0, function);
909 if (p == INVALID_POINTER)911 if (p == INVALID_POINTER)
910 bound_error("invalid pointer");912 bound_error("invalid pointer");
911}913}
@@ -915,8 +917,8 @@ void *__bound_memcpy(void *dst, const void *src, size_t size) · 3 unmodified lines
915 void* p;917 void* p;
916 918
917 INCR_COUNT(bound_mempcy_count);919 INCR_COUNT(bound_mempcy_count);
918 __bound_check(dst, size);920 __bound_check(dst, size, "memcpy");
919 __bound_check(src, size);921 __bound_check(src, size, "memcpy");
920 /* check also region overlap */922 /* check also region overlap */
921 if (no_checking == 0 && src >= dst && src < dst + size)923 if (no_checking == 0 && src >= dst && src < dst + size)
922 bound_error("overlapping regions in memcpy()");924 bound_error("overlapping regions in memcpy()");
@@ -929,23 +931,23 @@ void *__bound_memcpy(void *dst, const void *src, size_t size) · 6 unmodified lines
929int __bound_memcmp(const void *s1, const void *s2, size_t size)931int __bound_memcmp(const void *s1, const void *s2, size_t size)
930{932{
931 INCR_COUNT(bound_memcmp_count);933 INCR_COUNT(bound_memcmp_count);
932 __bound_check(s1, size);934 __bound_check(s1, size, "memcmp");
933 __bound_check(s2, size);935 __bound_check(s2, size, "memcmp");
934 return memcmp(s1, s2, size);936 return memcmp(s1, s2, size);
935}937}
936 938
937void *__bound_memmove(void *dst, const void *src, size_t size)939void *__bound_memmove(void *dst, const void *src, size_t size)
938{940{
939 INCR_COUNT(bound_memmove_count);941 INCR_COUNT(bound_memmove_count);
940 __bound_check(dst, size);942 __bound_check(dst, size, "memmove");
941 __bound_check(src, size);943 __bound_check(src, size, "memmove");
942 return memmove(dst, src, size);944 return memmove(dst, src, size);
943}945}
944 946
945void *__bound_memset(void *dst, int c, size_t size)947void *__bound_memset(void *dst, int c, size_t size)
946{948{
947 INCR_COUNT(bound_memset_count);949 INCR_COUNT(bound_memset_count);
948 __bound_check(dst, size);950 __bound_check(dst, size, "memset");
949 return memset(dst, c, size);951 return memset(dst, c, size);
950}952}
951 953
@@ -957,7 +959,7 @@ int __bound_strlen(const char *s) · 5 unmodified lines
957 INCR_COUNT(bound_strlen_count);959 INCR_COUNT(bound_strlen_count);
958 while (*p++);960 while (*p++);
959 len = (p - s) - 1;961 len = (p - s) - 1;
960 p = __bound_ptr_indir1((char *)s, len);962 p = __bound_ptr_indir1((char *)s, len, 0, "strlen");
961 if (p == INVALID_POINTER)963 if (p == INVALID_POINTER)
962 bound_error("bad pointer in strlen()");964 bound_error("bad pointer in strlen()");
963 return len;965 return len;
@@ -982,8 +984,8 @@ char *__bound_strncpy(char *dst, const char *src, size_t n) · 18 unmodified lines
982 while (len-- && *p++);984 while (len-- && *p++);
983 len = p - src;985 len = p - src;
984 INCR_COUNT(bound_strncpy_count);986 INCR_COUNT(bound_strncpy_count);
985 __bound_check(dst, len);987 __bound_check(dst, len, "strncpy");
986 __bound_check(src, len);988 __bound_check(src, len, "strncpy");
987 return strncpy (dst, src, n);989 return strncpy (dst, src, n);
988}990}
989 991
@@ -997,8 +999,8 @@ int __bound_strcmp(const char *s1, const char *s2) · 7 unmodified lines
997 u1++;999 u1++;
998 u2++;1000 u2++;
999 }1001 }
1000 __bound_check(s1, ((const char *)u1 - s1) + 1);1002 __bound_check(s1, ((const char *)u1 - s1) + 1, "strcmp");
1001 __bound_check(s2, ((const char *)u2 - s2) + 1);1003 __bound_check(s2, ((const char *)u2 - s2) + 1, "strcmp");
1002 return (*u1 - *u2);1004 return (*u1 - *u2);
1003}1005}
1004 1006
@@ -1018,8 +1020,8 @@ int __bound_strncmp(const char *s1, const char *s2, size_t n) · 13 unmodified lines
1018 }1020 }
1019 u2++;1021 u2++;
1020 } while (*u1++);1022 } while (*u1++);
1021 __bound_check(s1, ((const char *)u1 - s1) + 1);1023 __bound_check(s1, ((const char *)u1 - s1) + 1, "strncmp");
1022 __bound_check(s2, ((const char *)u1 - s1) + 1);1024 __bound_check(s2, ((const char *)u1 - s1) + 1, "strncmp");
1023 return retval;1025 return retval;
1024}1026}
1025 1027
@@ -1032,8 +1034,8 @@ char *__bound_strcat(char *dest, const char *src) · 6 unmodified lines
1032 while (*dest++);1034 while (*dest++);
1033 dest--;1035 dest--;
1034 while ((*dest++ = *src++) != 0);1036 while ((*dest++ = *src++) != 0);
1035 __bound_check(r, dest - r);1037 __bound_check(r, dest - r, "strcat");
1036 __bound_check(s, src - s);1038 __bound_check(s, src - s, "strcat");
1037 return r;1039 return r;
1038}1040}
1039 1041
@@ -1049,7 +1051,7 @@ char *__bound_strchr(const char *string, int ch) · 9 unmodified lines
1049 }1051 }
1050 s++;1052 s++;
1051 }1053 }
1052 __bound_check(string, ((const char *)s - string) + 1);1054 __bound_check(string, ((const char *)s - string) + 1, "strchr");
1053 return *s == c ? (char *) s : NULL;1055 return *s == c ? (char *) s : NULL;
1054}1056}
1055 1057
@@ -1060,7 +1062,7 @@ char *__bound_strdup(const char *s) · 4 unmodified lines
1060 1062
1061 INCR_COUNT(bound_strdup_count);1063 INCR_COUNT(bound_strdup_count);
1062 while (*p++);1064 while (*p++);
1063 __bound_check(s, p - s);1065 __bound_check(s, p - s, "strdup");
1064#if MALLOC_REDIR1066#if MALLOC_REDIR
1065 new = malloc_redir (p - s);1067 new = malloc_redir (p - s);
1066#else1068#else
@@ -1069,7 +1071,7 @@ char *__bound_strdup(const char *s) · 2 unmodified lines
1069 if (new) {1071 if (new) {
1070 WAIT_SEM ();1072 WAIT_SEM ();
1071 tree = splay_insert((size_t)new, p - s, tree);1073 tree = splay_insert((size_t)new, p - s, tree);
1072 if (tree->start == (size_t) new) {1074 if (tree && tree->start == (size_t) new) {
1073 tree->type = TCC_TYPE_STRDUP;1075 tree->type = TCC_TYPE_STRDUP;
1074 }1076 }
1075 memcpy (new, s, p - s);1077 memcpy (new, s, p - s);
@@ -1261,22 +1263,26 @@ static Tree * splay_insert(size_t addr, size_t size, Tree * t) · 185 unmodified lines
1261 new = (Tree *) malloc (sizeof (Tree));1263 new = (Tree *) malloc (sizeof (Tree));
1262#endif1264#endif
1263 }1265 }
1264 if (new == NULL) {bound_alloc_error();}1266 if (new == NULL) {
1265 if (t == NULL) {1267 bound_alloc_error();
1266 new->left = new->right = NULL;1268 }
1267 } else if (compare(addr, t->start, t->size) < 0) {1269 else {
1268 new->left = t->left;1270 if (t == NULL) {
1269 new->right = t;1271 new->left = new->right = NULL;
1270 t->left = NULL;1272 } else if (compare(addr, t->start, t->size) < 0) {
1271 } else {1273 new->left = t->left;
1272 new->right = t->right;1274 new->right = t;
1273 new->left = t;1275 t->left = NULL;
1274 t->right = NULL;1276 } else {
1277 new->right = t->right;
1278 new->left = t;
1279 t->right = NULL;
1280 }
1281 new->start = addr;
1282 new->size = size;
1283 new->type = TCC_TYPE_NONE;
1284 new->is_invalid = 0;
1275 }1285 }
1276 new->start = addr;
1277 new->size = size;
1278 new->type = TCC_TYPE_NONE;
1279 new->is_invalid = 0;
1280 return new;1286 return new;
1281}1287}
1282 1288
x86_64-gen.c+38−21
@@ -767,28 +767,12 @@ ST_FUNC void gen_bounded_ptr_add(void) · 766 unmodified lines
767 767
768 if (nested_call > 1) {768 if (nested_call > 1) {
769#ifdef TCC_TARGET_PE769#ifdef TCC_TARGET_PE
770 o(0x5152); /* push %rcx/%rdx */770 o(0x5152); /* push %rdx/%rcx */
771 o(0x51415041); /* push %r8/%r9 */
771#else772#else
772 o(0x5657); /* push %rdi/%rsi */773 o(0x51525657); /* push %rdi/%rsi/%rdx/%rcx */
773#endif774#endif
774 }775 }
775 /* trick to get line/file_name in code */
776 o(0xb8);
777 gen_le32 (file->line_num);
778 {
779 int len;
780 char *cp = file->filename;
781 while (*cp) cp++;
782 while (cp != file->filename && cp[-1] != '/') cp--;
783 len = strlen (cp);
784 while (len > 0) {
785 memcpy (&i, cp, 4);
786 o(0xb8);
787 gen_le32 (i);
788 cp += 4;
789 len -= 4;
790 }
791 }
792 /* prepare fast x86_64 function call */776 /* prepare fast x86_64 function call */
793 gv(RC_RAX);777 gv(RC_RAX);
794#ifdef TCC_TARGET_PE778#ifdef TCC_TARGET_PE
@@ -806,6 +790,38 @@ ST_FUNC void gen_bounded_ptr_add(void) · 11 unmodified lines
806#endif790#endif
807 vtop--;791 vtop--;
808 792
793 /* add line, filename */
794#ifdef TCC_TARGET_PE
795 o(0xc0c749); /* mov $xx,%r8 */
796#else
797 o(0xba); /* mov $xx,%edx */
798#endif
799 gen_le32 (file->line_num);
800 {
801 static addr_t offset;
802 static char last_filename[1024];
803 Sym *sym_data;
804
805 if (strcmp (last_filename, file->filename) != 0) {
806 void *ptr;
807 int len = strlen (file->filename) + 1;
808
809 offset = data_section->data_offset;
810 ptr = section_ptr_add(data_section, len);
811 memcpy (ptr, file->filename, len);
812 memcpy (last_filename, file->filename, len);
813 }
814#ifdef TCC_TARGET_PE
815 o(0xb949); /* mov $xx,%r9 */
816#else
817 o(0xb948); /* mov $xx,%rcx */
818#endif
819 gen_le64 (0);
820 sym_data = get_sym_ref(&char_pointer_type, data_section,
821 offset, data_section->data_offset);
822 greloca(cur_text_section, sym_data, ind - 8, R_X86_64_64, 0);
823 }
824
809#ifdef TCC_TARGET_PE825#ifdef TCC_TARGET_PE
810 o(0x20ec8348); /* sub $20, %rsp */826 o(0x20ec8348); /* sub $20, %rsp */
811#endif827#endif
@@ -819,9 +835,10 @@ ST_FUNC void gen_bounded_ptr_add(void) · 7 unmodified lines
819 835
820 if (nested_call > 1) {836 if (nested_call > 1) {
821#ifdef TCC_TARGET_PE837#ifdef TCC_TARGET_PE
822 o(0x5a59); /* pop %rcx/%rdx */838 o(0x58415941); /* pop %r9/%r8 */
839 o(0x5a59); /* pop %rcx/%rdx */
823#else840#else
824 o(0x5f5e); /* pop %rsi/%rdi */841 o(0x5f5e5a59); /* pop $rcx/%rdx/%rsi/%rdi */
825#endif842#endif
826 }843 }
827 /* returned pointer is in rax */844 /* returned pointer is in rax */

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t