Index: kernel/generic/src/mm/as.c
===================================================================
--- kernel/generic/src/mm/as.c	(revision f97f1e51a29f20f8f26f381c9c84567e9a997538)
+++ kernel/generic/src/mm/as.c	(revision 947958124af6213f29135ea2f78f9deee4f7480e)
@@ -285,8 +285,9 @@
 /** Check area conflicts with other areas.
  *
- * @param as    Address space.
- * @param addr  Starting virtual address of the area being tested.
- * @param count Number of pages in the area being tested.
- * @param avoid Do not touch this area.
+ * @param as      Address space.
+ * @param addr    Starting virtual address of the area being tested.
+ * @param count   Number of pages in the area being tested.
+ * @param guarded True if the area being tested is protected by guard pages.
+ * @param avoid   Do not touch this area.
  *
  * @return True if there is no conflict, false otherwise.
@@ -294,8 +295,15 @@
  */
 NO_TRACE static bool check_area_conflicts(as_t *as, uintptr_t addr,
-    size_t count, as_area_t *avoid)
+    size_t count, bool guarded, as_area_t *avoid)
 {
 	ASSERT((addr % PAGE_SIZE) == 0);
 	ASSERT(mutex_locked(&as->lock));
+
+	/*
+	 * If the addition of the supposed area address and size overflows,
+	 * report conflict.
+	 */
+	if (overflows_into_positive(addr, P2SZ(count)))
+		return false;
 	
 	/*
@@ -304,5 +312,5 @@
 	if (overlaps(addr, P2SZ(count), (uintptr_t) NULL, PAGE_SIZE))
 		return false;
-	
+
 	/*
 	 * The leaf node is found in O(log n), where n is proportional to
@@ -328,6 +336,51 @@
 		if (area != avoid) {
 			mutex_lock(&area->lock);
-			
+
+			/*
+			 * If at least one of the two areas are protected
+			 * by the AS_AREA_GUARD flag then we must be sure
+			 * that they are separated by at least one unmapped
+			 * page.
+			 */
+			int const gp = (guarded || 
+			    (area->flags & AS_AREA_GUARD)) ? 1 : 0;
+			
+			/*
+			 * The area comes from the left neighbour node, which
+			 * means that there already are some areas in the leaf
+			 * node, which in turn means that adding gp is safe and
+			 * will not cause an integer overflow.
+			 */
 			if (overlaps(addr, P2SZ(count), area->base,
+			    P2SZ(area->pages + gp))) {
+				mutex_unlock(&area->lock);
+				return false;
+			}
+			
+			mutex_unlock(&area->lock);
+		}
+	}
+	
+	node = btree_leaf_node_right_neighbour(&as->as_area_btree, leaf);
+	if (node) {
+		area = (as_area_t *) node->value[0];
+		
+		if (area != avoid) {
+			int gp;
+
+			mutex_lock(&area->lock);
+
+			gp = (guarded || (area->flags & AS_AREA_GUARD)) ? 1 : 0;
+			if (gp && overflows(addr, P2SZ(count))) {
+				/*
+				 * Guard page not needed if the supposed area
+				 * is adjacent to the end of the address space.
+				 * We already know that the following test is
+				 * going to fail...
+				 */
+				gp--;
+			}
+			
+			if (overlaps(addr, P2SZ(count + gp), area->base,
 			    P2SZ(area->pages))) {
 				mutex_unlock(&area->lock);
@@ -339,25 +392,10 @@
 	}
 	
-	node = btree_leaf_node_right_neighbour(&as->as_area_btree, leaf);
-	if (node) {
-		area = (as_area_t *) node->value[0];
-		
-		if (area != avoid) {
-			mutex_lock(&area->lock);
-			
-			if (overlaps(addr, P2SZ(count), area->base,
-			    P2SZ(area->pages))) {
-				mutex_unlock(&area->lock);
-				return false;
-			}
-			
-			mutex_unlock(&area->lock);
-		}
-	}
-	
 	/* Second, check the leaf node. */
 	btree_key_t i;
 	for (i = 0; i < leaf->keys; i++) {
 		area = (as_area_t *) leaf->value[i];
+		int agp;
+		int gp;
 		
 		if (area == avoid)
@@ -365,7 +403,18 @@
 		
 		mutex_lock(&area->lock);
-		
-		if (overlaps(addr, P2SZ(count), area->base,
-		    P2SZ(area->pages))) {
+
+		gp = (guarded || (area->flags & AS_AREA_GUARD)) ? 1 : 0;
+		agp = gp;
+
+		/*
+		 * Sanitize the two possible unsigned integer overflows.
+		 */
+		if (gp && overflows(addr, P2SZ(count)))
+			gp--;
+		if (agp && overflows(area->base, P2SZ(area->pages)))
+			agp--;
+
+		if (overlaps(addr, P2SZ(count + gp), area->base,
+		    P2SZ(area->pages + agp))) {
 			mutex_unlock(&area->lock);
 			return false;
@@ -392,7 +441,8 @@
  * this function.
  *
- * @param as    Address space.
- * @param bound Lowest address bound.
- * @param size  Requested size of the allocation.
+ * @param as      Address space.
+ * @param bound   Lowest address bound.
+ * @param size    Requested size of the allocation.
+ * @param guarded True if the allocation must be protected by guard pages.
  *
  * @return Address of the beginning of unmapped address space area.
@@ -401,5 +451,5 @@
  */
 NO_TRACE static uintptr_t as_get_unmapped_area(as_t *as, uintptr_t bound,
-    size_t size)
+    size_t size, bool guarded)
 {
 	ASSERT(mutex_locked(&as->lock));
@@ -423,7 +473,15 @@
 	/* First check the bound address itself */
 	uintptr_t addr = ALIGN_UP(bound, PAGE_SIZE);
-	if ((addr >= bound) &&
-	    (check_area_conflicts(as, addr, pages, NULL)))
-		return addr;
+	if (addr >= bound) {
+		if (guarded) {
+			/* Leave an unmapped page between the lower
+			 * bound and the area's start address.
+			 */
+			addr += P2SZ(1);
+		}
+
+		if (check_area_conflicts(as, addr, pages, guarded, NULL))
+			return addr;
+	}
 	
 	/* Eventually check the addresses behind each area */
@@ -439,7 +497,15 @@
 			addr =
 			    ALIGN_UP(area->base + P2SZ(area->pages), PAGE_SIZE);
+
+			if (guarded || area->flags & AS_AREA_GUARD) {
+				/* We must leave an unmapped page
+				 * between the two areas.
+				 */
+				addr += P2SZ(1);
+			}
+
 			bool avail =
 			    ((addr >= bound) && (addr >= area->base) &&
-			    (check_area_conflicts(as, addr, pages, area)));
+			    (check_area_conflicts(as, addr, pages, guarded, area)));
 			
 			mutex_unlock(&area->lock);
@@ -481,5 +547,5 @@
 	if (size == 0)
 		return NULL;
-	
+
 	size_t pages = SIZE2FRAMES(size);
 	
@@ -487,9 +553,11 @@
 	if ((flags & AS_AREA_EXEC) && (flags & AS_AREA_WRITE))
 		return NULL;
+
+	bool const guarded = flags & AS_AREA_GUARD;
 	
 	mutex_lock(&as->lock);
 	
 	if (*base == (uintptr_t) -1) {
-		*base = as_get_unmapped_area(as, bound, size);
+		*base = as_get_unmapped_area(as, bound, size, guarded);
 		if (*base == (uintptr_t) -1) {
 			mutex_unlock(&as->lock);
@@ -497,6 +565,9 @@
 		}
 	}
-	
-	if (!check_area_conflicts(as, *base, pages, NULL)) {
+
+	if (overflows_into_positive(*base, size))
+		return NULL;
+
+	if (!check_area_conflicts(as, *base, pages, guarded, NULL)) {
 		mutex_unlock(&as->lock);
 		return NULL;
@@ -665,10 +736,4 @@
 		
 		page_table_lock(as, false);
-		
-		/*
-		 * Start TLB shootdown sequence.
-		 */
-		ipl_t ipl = tlb_shootdown_start(TLB_INVL_PAGES, as->asid,
-		    area->base + P2SZ(pages), area->pages - pages);
 		
 		/*
@@ -726,4 +791,20 @@
 				}
 				
+				/*
+				 * Start TLB shootdown sequence.
+				 *
+				 * The sequence is rather short and can be
+				 * repeated multiple times. The reason is that
+				 * we don't want to have used_space_remove()
+				 * inside the sequence as it may use a blocking
+				 * memory allocation for its B+tree. Blocking
+				 * while holding the tlblock spinlock is
+				 * forbidden and would hit a kernel assertion.
+				 */
+
+				ipl_t ipl = tlb_shootdown_start(TLB_INVL_PAGES,
+				    as->asid, area->base + P2SZ(pages),
+				    area->pages - pages);
+		
 				for (; i < size; i++) {
 					pte_t *pte = page_mapping_find(as,
@@ -743,29 +824,37 @@
 					page_mapping_remove(as, ptr + P2SZ(i));
 				}
+		
+				/*
+				 * Finish TLB shootdown sequence.
+				 */
+		
+				tlb_invalidate_pages(as->asid,
+				    area->base + P2SZ(pages),
+				    area->pages - pages);
+		
+				/*
+				 * Invalidate software translation caches
+				 * (e.g. TSB on sparc64, PHT on ppc32).
+				 */
+				as_invalidate_translation_cache(as,
+				    area->base + P2SZ(pages),
+				    area->pages - pages);
+				tlb_shootdown_finalize(ipl);
 			}
 		}
-		
-		/*
-		 * Finish TLB shootdown sequence.
-		 */
-		
-		tlb_invalidate_pages(as->asid, area->base + P2SZ(pages),
-		    area->pages - pages);
-		
-		/*
-		 * Invalidate software translation caches
-		 * (e.g. TSB on sparc64, PHT on ppc32).
-		 */
-		as_invalidate_translation_cache(as, area->base + P2SZ(pages),
-		    area->pages - pages);
-		tlb_shootdown_finalize(ipl);
-		
 		page_table_unlock(as, false);
 	} else {
 		/*
 		 * Growing the area.
+		 */
+
+		if (overflows_into_positive(address, P2SZ(pages)))
+			return EINVAL;
+
+		/*
 		 * Check for overlaps with other address space areas.
 		 */
-		if (!check_area_conflicts(as, address, pages, area)) {
+		bool const guarded = area->flags & AS_AREA_GUARD;
+		if (!check_area_conflicts(as, address, pages, guarded, area)) {
 			mutex_unlock(&area->lock);
 			mutex_unlock(&as->lock);
