diff --git a/share/libc/string.h b/share/libc/string.h
index d021f5a873f05884e6529b23552252a86c65b170..0d6379e24f01985a15df5af17a7476d6b8412b82 100644
--- a/share/libc/string.h
+++ b/share/libc/string.h
@@ -499,6 +499,21 @@ extern size_t strlcat(char *restrict dest, const char *restrict src, size_t n);
 extern size_t strxfrm (char *restrict dest,
 		       const char *restrict src, size_t n);
 
+// Non-POSIX; GNU extension
+/*@
+  requires valid_haystack: \valid_read((char*)haystack + (0 .. haystacklen-1));
+  requires valid_needle: \valid_read((char*)needle + (0 .. needlelen-1));
+  assigns \result \from haystack,
+                        indirect:((char*)haystack)[0 .. haystacklen-1],
+                        indirect:((char*)needle)[0 .. needlelen-1];
+  ensures result_null_or_valid:
+    \result == \null || \valid_read((char*)\result);
+  ensures result_null_or_same_base:
+    \result == \null || \base_addr(\result) == \base_addr(haystack);
+*/
+extern void *memmem(const void *haystack, size_t haystacklen,
+             const void *needle, size_t needlelen);
+
 // Allocate strings
 
 /*@ requires valid_string_s: valid_read_string(s);
diff --git a/tests/libc/string_h.c b/tests/libc/string_h.c
index d51e38c9c28fa26aa147cecf4a7ce431c104054d..090b9f60561255b05a84c2510a8cabed3092f459 100644
--- a/tests/libc/string_h.c
+++ b/tests/libc/string_h.c
@@ -174,5 +174,15 @@ int main(int argc, char **argv)
   rchr = memrchr(c, 'n', strlen(c));
   //@ check imprecise: rchr == \null;
 
+  char mm_haystack[] = { 'I', 'h', 'a', 'v', 'e', '\0', 'z', 'e', 'r', 'o' };
+  char mm_needle[] = { 'z', 'e', 'r', 'o' };
+  char *memm =
+    memmem(mm_haystack, sizeof(mm_haystack), mm_needle, sizeof(mm_needle));
+  //@ check imprecise: memm == mm_haystack + 6;
+  char mm_needle2[] = { '0' };
+  memm =
+    memmem(mm_haystack, sizeof(mm_haystack), mm_needle2, sizeof(mm_needle2));
+  //@ check imprecise: memm == \null;
+
   return 0;
 }