whats the purpose of x86 cr0 WP bit?
Quoting from Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3A pg. 2-15 (Emphasis mine):
WP Write Protect (bit 16 of CR0) — When set, inhibits supervisor-level procedures from writing into readonly pages; when clear, allows supervisor-level procedures to write into read-only pages (regardless of the U/S bit setting; see Section 4.1.3 and Section 4.6). This flag facilitates implementation of the copy-on-write method of creating a new process (forking) used by operating systems such as UNIX.
Update: Looking at wikipedia on fork():
Whenever a process (parent or child) modifies a page, a separate copy of that particular page alone is made for that process (parent or child) which performed the modification.
This is at the core of copy-on-write, but presents a problem when the modification is done by the kernel (such as when the write occurs as a result of syscall - think read()
).
From 4.1.3:
CR0.WP allows pages to be protected from supervisor-mode writes. If CR0.WP = 0, supervisor-mode write accesses are allowed to linear addresses with read-only access rights; if CR0.WP = 1, they are not. (User-mode write accesses are never allowed to linear addresses with read-only access rights, regardless of the value of CR0.WP.)
By setting CR0.WP = 1
the kernel will be notified (with a page-fault) when it modifies read-only user pages and can perform the copy-on-write operation before proceeding with the page modification.
WP=1 is the default. It enables traps when kernel writes to a read only (write protected) user (U) page, so the kernel can check whether it is a COW page (so it knows that another copy of the page needs to be created that is not COW), otherwise it would have to check every single memory address it is accessing or it would break COW by making the change visible to other processes that map the memory.
WP can be set to 0 (but it wants to make sure interrupts are disabled / masked during this handling, so that control can't be taken away by code executing that is unaware of this condition). This allows a write protected cr3 page to be unwriteprotected (because when you unset the write bit in its recursive PML4 entry to itself, nothing can be done about it until you WP = 0, otherwise it will keep causing a trap)