I've explored the following case:
$ printf '12\n34\0' | LC_ALL=en_US.utf-8 grep -z '^[1-4]*$' | wc -c
6
It's a bug (there should be no match).

This is what grep does:
I think this should be worked around in grep: before calling 're_search' it should split the input string by 'eolbyte'.

The bug also present with PCRE engine:
$ printf '12\n34\0' | LC_ALL=en_US.utf-8 grep -z -P '^[1234]*$' | wc -c
6
$ printf '12\n34\0' | LC_ALL=en_US.utf-8 grep -z -P '^[1-4]*$' | wc -c
6

Ulya