diff --git a/mm/ksm.c b/mm/ksm.c index 5621840401c0..64b054641f39 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -1392,14 +1392,18 @@ static struct stable_node *stable_node_dup(struct stable_node **_stable_node, ksm_stable_node_chains--; ksm_stable_node_dups--; /* - * NOTE: the caller depends on the - * *_stable_node to become NULL if the chain - * was collapsed. Enforce that if anything - * uses a stale (freed) stable_node chain a - * visible crash will materialize (instead of - * an use after free). + * NOTE: the caller depends on the stable_node + * to be equal to stable_node_dup if the chain + * was collapsed. */ - *_stable_node = stable_node = NULL; + *_stable_node = found; + /* + * Just for robustneess as stable_node is + * otherwise left as a stable pointer, the + * compiler shall optimize it away at build + * time. + */ + stable_node = NULL; } else if (__is_page_sharing_candidate(found, 1)) { /* * Refile our candidate at the head @@ -1505,7 +1509,11 @@ static struct page *stable_tree_search(struct page *page) * not NULL. stable_node_dup may have been inserted in * the rbtree instead as a regular stable_node (in * order to collapse the stable_node chain if a single - * stable_node dup was found in it). + * stable_node dup was found in it). In such case the + * stable_node is overwritten by the calleee to point + * to the stable_node_dup that was collapsed in the + * stable rbtree and stable_node will be equal to + * stable_node_dup like if the chain never existed. */ if (!stable_node_dup) { /* @@ -1623,15 +1631,13 @@ static struct page *stable_tree_search(struct page *page) replace: /* * If stable_node was a chain and chain_prune collapsed it, - * stable_node will be NULL here. In that case the - * stable_node_dup is the regular stable_node that has - * replaced the chain. If stable_node is not NULL and equal to - * stable_node_dup there was no chain and stable_node_dup is - * the regular stable_node in the stable rbtree. Otherwise - * stable_node is the chain and stable_node_dup is the dup to - * replace. + * stable_node has been updated to be the new regular + * stable_node. A collapse of the chain is indistinguishable + * from the case there was no chain in the stable + * rbtree. Otherwise stable_node is the chain and + * stable_node_dup is the dup to replace. */ - if (!stable_node || stable_node_dup == stable_node) { + if (stable_node_dup == stable_node) { VM_BUG_ON(is_stable_node_chain(stable_node_dup)); VM_BUG_ON(is_stable_node_dup(stable_node_dup)); /* there is no chain */ @@ -1676,13 +1682,13 @@ static struct page *stable_tree_search(struct page *page) stable_node_dup = stable_node_any; /* * If stable_node was a chain and chain_prune collapsed it, - * stable_node will be NULL here. In that case the - * stable_node_dup is the regular stable_node that has - * replaced the chain. If stable_node is not NULL and equal to - * stable_node_dup there was no chain and stable_node_dup is - * the regular stable_node in the stable rbtree. + * stable_node has been updated to be the new regular + * stable_node. A collapse of the chain is indistinguishable + * from the case there was no chain in the stable + * rbtree. Otherwise stable_node is the chain and + * stable_node_dup is the dup to replace. */ - if (!stable_node || stable_node_dup == stable_node) { + if (stable_node_dup == stable_node) { VM_BUG_ON(is_stable_node_chain(stable_node_dup)); VM_BUG_ON(is_stable_node_dup(stable_node_dup)); /* chain is missing so create it */