diff --git a/share/libc/__fc_string_axiomatic.h b/share/libc/__fc_string_axiomatic.h
index a89a527466eacc4412932d441b23dbfbb3817c69..efd4b09bc698630ed51de4c0ae6bdece653e5e11 100644
--- a/share/libc/__fc_string_axiomatic.h
+++ b/share/libc/__fc_string_axiomatic.h
@@ -171,6 +171,21 @@ __BEGIN_DECLS
   @ }
   @*/
 
+/*@ axiomatic WMemChr {
+  @ logic 𝔹 wmemchr{L}(wchar_t *s, wchar_t c, ℤ n)
+  @   reads s[0..n - 1];
+  @ // Returns [true] iff wide char array [s] contains wide character [c]
+  @
+  @ logic ℤ wmemchr_off{L}(wchar_t *s, wchar_t c, ℤ n)
+  @   reads s[0..n - 1];
+  @ // Returns the offset at which [c] appears in [s].
+  @
+  @ axiom wmemchr_def{L}:
+  @   \forall wchar_t *s; \forall wchar_t c; \forall ℤ n;
+  @      wmemchr(s,c,n) <==> \exists int i; 0 <= i < n && s[i] == c;
+  @ }
+  @*/
+
 /*@ axiomatic WcsLen {
   @ logic ℤ wcslen{L}(wchar_t *s)
   @   reads s[0..];
diff --git a/share/libc/wchar.h b/share/libc/wchar.h
index ff7e863ad8de1e7b5cf5c657bfe7f17d873ceadd..fb07265633362a1736cde59aac0f3f83d1bf365d 100644
--- a/share/libc/wchar.h
+++ b/share/libc/wchar.h
@@ -41,6 +41,8 @@ __PUSH_FC_STDLIB
 // ISO C requires the tag 'struct tm' (as declared in <time.h>) to be declared.
 #include "time.h"
 
+#include "string.h"
+
 __BEGIN_DECLS
 
 #ifndef WEOF
@@ -48,16 +50,35 @@ __BEGIN_DECLS
 #endif
 
 /*@
+  requires valid:
+    valid_read_or_empty((char*)s, (size_t)(sizeof(wchar_t)*n))
+    || \valid_read(((unsigned char*)s)+(0..wmemchr_off(s,c,n)));
+  @ requires initialization:
+        \initialized(s+(0..n - 1))
+     || \initialized(s+(0..wmemchr_off(s,c,n)));
+  @ requires danglingness:
+        non_escaping(s, (size_t)(sizeof(wchar_t)*n))
+     || non_escaping(s, (size_t)(sizeof(wchar_t)*(wmemchr_off(s,c,n)+1)));
   assigns \result \from s, indirect:s[0 .. n-1], indirect:c, indirect:n;
   ensures result_null_or_inside_s:
     \result == \null || \subset (\result, s+(0 .. n-1));
  */
 extern wchar_t * wmemchr(const wchar_t *s, wchar_t c, size_t n);
 
-/*@ assigns \result \from indirect:s1[0 .. n-1], indirect:s2[0 .. n-1], indirect:n; */
+/*@
+  requires valid_s1: valid_read_or_empty(s1, (size_t)(sizeof(wchar_t)*n));
+  requires valid_s2: valid_read_or_empty(s2, (size_t)(sizeof(wchar_t)*n));
+  requires initialization:s1: \initialized(s1+(0..n-1));
+  requires initialization:s2: \initialized(s2+(0..n-1));
+  requires danglingness:s1: non_escaping(s1, (size_t)(sizeof(wchar_t)*n));
+  requires danglingness:s2: non_escaping(s2, (size_t)(sizeof(wchar_t)*n));
+  assigns \result \from indirect:s1[0 .. n-1], indirect:s2[0 .. n-1], indirect:n;
+*/
 extern int wmemcmp(const wchar_t *s1, const wchar_t *s2, size_t n);
 
 /*@
+  requires valid_dest: valid_or_empty(dest, (size_t)(sizeof(wchar_t)*n));
+  requires valid_src: valid_read_or_empty(src, (size_t)(sizeof(wchar_t)*n));
   requires separation:dest:src: \separated(dest+(0 .. n-1), src+(0 .. n-1));
   assigns dest[0 .. n-1] \from src[0 .. n-1], indirect:src, indirect:n;
   assigns \result \from dest;
@@ -66,6 +87,8 @@ extern int wmemcmp(const wchar_t *s1, const wchar_t *s2, size_t n);
 extern wchar_t * wmemcpy(wchar_t *restrict dest, const wchar_t *restrict src, size_t n);
 
 /*@
+  requires valid_src: \valid_read(src+(0..n-1));
+  requires valid_dest: \valid(dest+(0..n-1));
   assigns dest[0 .. n-1] \from src[0 .. n-1], indirect:src, indirect:n;
   assigns \result \from dest;
   ensures result_ptr: \result == dest;
@@ -73,6 +96,7 @@ extern wchar_t * wmemcpy(wchar_t *restrict dest, const wchar_t *restrict src, si
 extern wchar_t * wmemmove(wchar_t *dest, const wchar_t *src, size_t n);
 
 /*@
+  requires valid_wcs: \valid(wcs+(0..n-1));
   assigns wcs[0 .. n-1] \from wc, indirect:n;
   assigns \result \from wcs;
   ensures result_ptr: \result == wcs;
@@ -82,6 +106,10 @@ extern wchar_t * wmemmove(wchar_t *dest, const wchar_t *src, size_t n);
 extern wchar_t * wmemset(wchar_t *wcs, wchar_t wc, size_t n);
 
 /*@
+  requires valid_wstring_src: valid_read_wstring(src);
+  requires valid_wstring_dest: valid_wstring(dest);
+  requires room_for_concatenation: \valid(dest+(wcslen(dest)..wcslen(dest)+wcslen(src)));
+  requires separation:\separated(dest+(0..wcslen(dest)+wcslen(src)),src+(0..wcslen(src)));
   assigns dest[0 .. ] \from dest[0 .. ], indirect:dest, src[0 .. ], indirect:src;
   assigns \result \from dest;
   ensures result_ptr: \result == dest;
@@ -96,21 +124,36 @@ extern wchar_t * wcscat(wchar_t *restrict dest, const wchar_t *restrict src);
 */
 extern wchar_t * wcschr(const wchar_t *wcs, wchar_t wc);
 
-/*@ assigns \result \from indirect:s1[0 .. ], indirect:s2[0 .. ]; */
+/*@
+  requires valid_wstring_s1: valid_read_wstring(s1); // over-strong
+  requires valid_wstring_s2: valid_read_wstring(s2); // over-strong
+  assigns \result \from indirect:s1[0 .. ], indirect:s2[0 .. ];
+*/
 extern int wcscmp(const wchar_t *s1, const wchar_t *s2);
 
 /*@
-  assigns dest[0 .. ] \from src[0 .. ], indirect:src, dest[0 .. ], indirect:dest;
+  requires valid_wstring_src: valid_read_wstring(src);
+  requires room_wstring: \valid(dest+(0 .. wcslen(src)));
+  requires separation:\separated(dest+(0..wcslen(src)),src+(0..wcslen(src)));
+  assigns dest[0 .. wcslen(src)] \from src[0 .. wcslen(src)], indirect:src;
   assigns \result \from dest;
   ensures result_ptr: \result == dest;
  */
 extern wchar_t * wcscpy(wchar_t *restrict dest, const wchar_t *restrict src);
 
-/*@ assigns \result \from indirect:wcs[0 .. ], indirect:accept[0 .. ]; */
+/*@
+  requires valid_wstring_wcs: valid_read_wstring(wcs);
+  requires valid_wstring_accept: valid_read_wstring(accept);
+  assigns \result \from indirect:wcs[0 .. ], indirect:accept[0 .. ];
+ */
 extern size_t wcscspn(const wchar_t *wcs, const wchar_t *accept);
 
 // wcslcat is a BSD extension (non-C99, non-POSIX)
 /*@
+  requires valid_nwstring_src: valid_read_nwstring(src, n);
+  requires valid_wstring_dest: valid_wstring(dest);
+  requires room_for_concatenation: \valid(dest+(wcslen(dest)..wcslen(dest)+\min(wcslen(src), n)));
+  requires separation:\separated(dest+(0..wcslen(dest)+wcslen(src)),src+(0..wcslen(src)));
   assigns dest[0 .. ] \from dest[0 .. ], indirect:dest, src[0 .. n-1], indirect:src, indirect:n;
   assigns \result \from indirect:dest[0 .. ], indirect:src[0 .. n-1], indirect:n;
 */
@@ -118,6 +161,8 @@ extern size_t wcslcat(wchar_t *restrict dest, const wchar_t *restrict src, size_
 
 // wcslcpy is a BSD extension (non-C99, non-POSIX)
 /*@
+  requires valid_wstring_src: valid_read_wstring(src);
+  requires room_nwstring: \valid(dest+(0 .. n));
   requires separation:dest:src: \separated(dest+(0 .. n-1), src+(0 .. n-1));
   assigns dest[0 .. n-1] \from src[0 .. n-1], indirect:src, indirect:n;
   assigns \result \from indirect:dest[0 .. n-1], indirect:dest,
@@ -125,29 +170,45 @@ extern size_t wcslcat(wchar_t *restrict dest, const wchar_t *restrict src, size_
  */
 extern size_t wcslcpy(wchar_t *dest, const wchar_t *src, size_t n);
 
-/*@ requires valid_string_s: valid_read_wstring(s);
-  assigns \result \from indirect:s[0 .. ]; */
+/*@
+  requires valid_string_s: valid_read_wstring(s);
+  assigns \result \from indirect:s[0 .. wcslen(s)];
+  ensures result_is_length: \result == wcslen(s);
+ */
 extern size_t wcslen(const wchar_t *s);
 
 /*@
+  requires valid_nwstring_src: valid_read_nwstring(src, n);
+  requires valid_wstring_dest: valid_wstring(dest);
+  requires room_for_concatenation: \valid(dest+(wcslen(dest)..wcslen(dest)+\min(wcslen(src), n)));
+  requires separation:\separated(dest+(0..wcslen(dest)+wcslen(src)),src+(0..wcslen(src)));
   assigns dest[0 .. ] \from dest[0 .. ], indirect:dest, src[0 .. n-1], indirect:src, indirect:n;
   assigns \result \from dest;
   ensures result_ptr: \result == dest;
 */
 extern wchar_t * wcsncat(wchar_t *restrict dest, const wchar_t *restrict src, size_t n);
 
-/*@ assigns \result \from indirect:s1[0 .. n-1], indirect:s2[0 .. n-1], indirect:n; */
+/*@
+  requires valid_wstring_s1: valid_read_wstring(s1); // over-strong
+  requires valid_wstring_s2: valid_read_wstring(s2); // over-strong
+  assigns \result \from indirect:s1[0 .. n-1], indirect:s2[0 .. n-1], indirect:n;
+*/
 extern int wcsncmp(const wchar_t *s1, const wchar_t *s2, size_t n);
 
 /*@
+  requires valid_wstring_src: valid_read_wstring(src);
+  requires room_nwstring: \valid(dest+(0 .. n-1));
   requires separation:dest:src: \separated(dest+(0 .. n-1), src+(0 .. n-1));
   assigns dest[0 .. n-1] \from src[0 .. n-1], indirect:src, indirect:n;
   assigns \result \from dest;
   ensures result_ptr: \result == dest;
+  ensures initialization: \initialized(dest+(0 .. n-1));
  */
 extern wchar_t * wcsncpy(wchar_t *restrict dest, const wchar_t *restrict src, size_t n);
 
 /*@
+  requires valid_wstring_wcs: valid_read_wstring(wcs);
+  requires valid_wstring_accept: valid_read_wstring(accept);
   assigns \result \from wcs, indirect:wcs[0 .. ], indirect:accept[0 .. ];
   ensures result_null_or_inside_wcs:
     \result == \null || \subset (\result, wcs+(0 .. ));
@@ -155,16 +216,24 @@ extern wchar_t * wcsncpy(wchar_t *restrict dest, const wchar_t *restrict src, si
 extern wchar_t * wcspbrk(const wchar_t *wcs, const wchar_t *accept);
 
 /*@
-  assigns \result \from wcs, indirect:wcs[0 .. ], indirect:wc;
+  requires valid_wstring_wcs: valid_read_wstring(wcs);
+  assigns \result \from wcs, indirect:wcs[0 .. wcslen(wcs)], indirect:wc;
   ensures result_null_or_inside_wcs:
     \result == \null || \subset (\result, wcs+(0 .. ));
  */
 extern wchar_t * wcsrchr(const wchar_t *wcs, wchar_t wc);
 
-/*@ assigns \result \from indirect:wcs[0 .. ], indirect:accept[0 .. ]; */
+/*@
+  requires valid_wstring_wcs: valid_read_wstring(wcs);
+  requires valid_wstring_accept: valid_read_wstring(accept);
+  assigns \result \from indirect:wcs[0 .. wcslen(wcs)],
+                        indirect:accept[0 .. wcslen(accept)];
+*/
 extern size_t wcsspn(const wchar_t *wcs, const wchar_t *accept);
 
 /*@
+  requires valid_wstring_haystack: valid_read_wstring(haystack);
+  requires valid_wstring_needle: valid_read_wstring(needle);
   assigns \result \from haystack, indirect:haystack[0 .. ], indirect:needle[0 .. ];
   ensures result_null_or_inside_haystack:
     \result == \null || \subset (\result, haystack+(0 .. ));
@@ -172,8 +241,9 @@ extern size_t wcsspn(const wchar_t *wcs, const wchar_t *accept);
 extern wchar_t * wcsstr(const wchar_t *haystack, const wchar_t *needle);
 
 /*@
+  requires room_nwstring: \valid(ws+(0..n-1));
   requires valid_stream: \valid(stream);
-  assigns ws[0..n] \from indirect:n, indirect:*stream;
+  assigns ws[0..n-1] \from indirect:n, indirect:*stream;
   assigns \result \from ws, indirect:n, indirect:*stream;
   ensures result_null_or_same: \result == \null || \result == ws;
   ensures terminated_string_on_success:
diff --git a/tests/libc/wchar_h.c b/tests/libc/wchar_h.c
index c10e733da9168c3ffb7537e5c97faf20d8727105..e7cb493fa75d8adf0b06af406ebdf64c38dd3463 100644
--- a/tests/libc/wchar_h.c
+++ b/tests/libc/wchar_h.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 #include <wchar.h>
-
+volatile int v;
 int main() {
   FILE *fd = fopen("bla", "r");
   if (!fd) return 1;
@@ -8,5 +8,63 @@ int main() {
   wchar_t *res = fgetws(buf, 29, fd);
   if (!res) return 1;
   //@ assert res == buf;
+  wchar_t buf2[2];
+  buf2[0] = L'a';
+  wchar_t *r = wmemchr(buf2, L'a', 2); // no warning
+  //@ check ok: r != \null;
+  r = wmemchr(0, 0, 0); // should be ok
+  //@ check ok: r == \null;
+  if (v) {
+    r = wmemchr(buf2, 0, 2); // red alarm (uninit)
+    //@ assert unreachable:\false;
+  }
+  r = wmemchr(buf2, L'a', 3); // no warning
+  //@ check ok: r != \null;
+  if (v) buf2[1] = L'b';
+  r = wmemchr(buf2, L'a', 3); // no warning
+  //@ check ok: r != \null;
+  r = wmemchr(buf2, L'b', 3); // warning: buf2[1] maybe uninit
+  //@ check ok: r != \null;
+  buf2[1] = L'b';
+  r = wmemchr(buf2, L'b', 3); // no warning
+  //@ check ok: r != \null;
+  wchar_t *wsrc = L"wide thing";
+  wchar_t wdst[10];
+  r = wcsncpy(wdst, wsrc, 10); // no warning
+  //@ check ok: r == wdst;
+  //@ check ok: \initialized(&wdst[9]);
+  if (v) {
+    r = wcsncpy(wdst, wsrc, wcslen(wsrc)+1); // error: not enough room
+    //@ assert unreachable:\false;
+  }
+  if (v) {
+    wcsncpy(wdst, wdst, 10); // error: no separation
+    //@ assert unreachable:\false;
+  }
+  if (v) {
+    wcsncpy(0, wsrc, 10); // error: invalid dest
+    //@ assert unreachable:\false;
+  }
+  if (v) {
+    wcsncpy(wdst, 0, 10); // error: invalid src
+    //@ assert unreachable:\false;
+  }
+  if (v) {
+    wcsncpy(wsrc, wdst, 10); // error: non-writable dest
+    //@ assert unreachable:\false;
+  }
+  wcsncmp(wsrc, wsrc, 11); // no warning
+  wcsncmp(wsrc, wdst, 11); // warning: wdst possibly invalid
+  wchar_t wdst2[20] = {0};
+  wcsncat(wdst2, wsrc, 11); // no warning
+  wcsncat(wdst2, wsrc, 10); // no warning (if wdst2 is precise)
+  //@ loop unroll 10;
+  for (int i = 0; i < 10; i++)
+    wdst2[i] = L'A';
+  wdst2[10] = L'\0'; // wdst2 now has length 10
+  if (v) {
+    wcsncat(wdst2+10, wdst2, 10); // error: no separation
+    //@ assert unreachable:\false;
+  }
   return 0;
 }