--- old/usr/src/cmd/mdb/common/modules/genunix/genunix.c	Wed Nov 20 17:16:45 2019
+++ new/usr/src/cmd/mdb/common/modules/genunix/genunix.c	Wed Nov 20 17:16:45 2019
@@ -4293,8 +4293,9 @@
 	{ "bufctl", ":[-vh] [-a addr] [-c caller] [-e earliest] [-l latest] "
 		"[-t thd]", "print or filter a bufctl", bufctl, bufctl_help },
 	{ "freedby", ":", "given a thread, print its freed buffers", freedby },
-	{ "kmalog", "?[ fail | slab ]",
-	    "display kmem transaction log and stack traces", kmalog },
+	{ "kmalog", "?[ fail | slab | zerosized ]",
+	    "display kmem transaction log and stack traces for specified type",
+	    kmalog },
 	{ "kmastat", "[-kmg]", "kernel memory allocator stats",
 	    kmastat },
 	{ "kmausers", "?[-ef] [cache ...]", "current medium and large users "
--- old/usr/src/cmd/mdb/common/modules/genunix/kmem.c	Wed Nov 20 17:16:47 2019
+++ new/usr/src/cmd/mdb/common/modules/genunix/kmem.c	Wed Nov 20 17:16:47 2019
@@ -3917,6 +3917,8 @@
 			logname = "kmem_failure_log";
 		else if (strcmp(argv->a_un.a_str, "slab") == 0)
 			logname = "kmem_slab_log";
+		else if (strcmp(argv->a_un.a_str, "zerosized") == 0)
+			logname = "kmem_zerosized_log";
 		else
 			return (DCMD_USAGE);
 	}
--- old/usr/src/man/man9f/kmem_alloc.9f	Wed Nov 20 17:16:49 2019
+++ new/usr/src/man/man9f/kmem_alloc.9f	Wed Nov 20 17:16:49 2019
@@ -5,11 +5,10 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH KMEM_ALLOC 9F "Oct 22, 2014"
+.TH KMEM_ALLOC 9F "Nov 20, 2019"
 .SH NAME
 kmem_alloc, kmem_zalloc, kmem_free \- allocate kernel memory
 .SH SYNOPSIS
-.LP
 .nf
 #include <sys/types.h>
 #include <sys/kmem.h>
@@ -30,11 +29,8 @@
 .fi
 
 .SH INTERFACE LEVEL
-.sp
-.LP
 Architecture independent level 1 (DDI/DKI).
 .SH PARAMETERS
-.sp
 .ne 2
 .na
 \fB\fIsize\fR\fR
@@ -64,8 +60,6 @@
 .RE
 
 .SH DESCRIPTION
-.sp
-.LP
 The \fBkmem_alloc()\fR function allocates \fIsize\fR bytes of kernel memory and
 returns a pointer to the allocated memory. The allocated memory is at least
 double-word aligned, so it can hold any C data structure. No greater alignment
@@ -84,28 +78,20 @@
 buffer address and size must exactly match the original allocation. Memory
 cannot be returned piecemeal.
 .SH RETURN VALUES
-.sp
-.LP
 If successful, \fBkmem_alloc()\fR and \fBkmem_zalloc()\fR return a pointer to
 the allocated memory. If \fBKM_NOSLEEP\fR is set and memory cannot be allocated
 without sleeping, \fBkmem_alloc()\fR and \fBkmem_zalloc()\fR return \fINULL\fR.
 .SH CONTEXT
-.sp
-.LP
 The \fBkmem_alloc()\fR and \fBkmem_zalloc()\fR functions can be called from
 interrupt context only if the \fBKM_NOSLEEP\fR flag is set. They can be called
 from user context with any valid \fIflag\fR. The \fBkmem_free()\fR function can
 be called from from user, interrupt, or kernel context.
 .SH SEE ALSO
-.sp
-.LP
 \fBcopyout\fR(9F), \fBfreerbuf\fR(9F), \fBgetrbuf\fR(9F)
 .sp
 .LP
 \fIWriting Device Drivers\fR
 .SH WARNINGS
-.sp
-.LP
 Memory allocated using \fBkmem_alloc()\fR is not paged. Available memory is
 therefore limited by the total physical memory on the system. It is also
 limited by the available kernel virtual address space, which is often the more
@@ -127,7 +113,8 @@
 uninitialized kernel memory should be handled carefully. For example, never
 \fBcopyout\fR(9F) a potentially uninitialized buffer.
 .SH NOTES
-.sp
-.LP
-\fBkmem_alloc(0\fR, \fIflag\fR\fB)\fR always returns \fINULL\fR.
-\fBkmem_free(NULL, 0)\fR is legal.
+\fBkmem_alloc(0\fR, \fIflag\fR\fB)\fR always returns \fINULL\fR, but
+if \fBKM_SLEEP\fR is set, this behavior is considered to be deprecated;
+the system may be configured to explicitly panic in this case in lieu
+of returning \fINULL\fR.
+\fBkmem_free(NULL, 0)\fR is legal, however.
--- old/usr/src/uts/common/fs/nfs/nfs_auth.c	Wed Nov 20 17:16:52 2019
+++ new/usr/src/uts/common/fs/nfs/nfs_auth.c	Wed Nov 20 17:16:52 2019
@@ -23,6 +23,7 @@
  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2015 by Delphix. All rights reserved.
+ * Copyright (c) 2015 Joyent, Inc.  All rights reserved.
  */
 
 #include <sys/param.h>
@@ -561,11 +562,16 @@
 			*access = res.ares.auth_perm;
 			*srv_uid = res.ares.auth_srv_uid;
 			*srv_gid = res.ares.auth_srv_gid;
-			*srv_gids_cnt = res.ares.auth_srv_gids.len;
-			*srv_gids = kmem_alloc(*srv_gids_cnt * sizeof (gid_t),
-			    KM_SLEEP);
-			bcopy(res.ares.auth_srv_gids.val, *srv_gids,
-			    *srv_gids_cnt * sizeof (gid_t));
+
+			if ((*srv_gids_cnt = res.ares.auth_srv_gids.len) != 0) {
+				*srv_gids = kmem_alloc(*srv_gids_cnt *
+				    sizeof (gid_t), KM_SLEEP);
+				bcopy(res.ares.auth_srv_gids.val, *srv_gids,
+				    *srv_gids_cnt * sizeof (gid_t));
+			} else {
+				*srv_gids = NULL;
+			}
+
 			break;
 
 		case NFSAUTH_DR_EFAIL:
@@ -1054,9 +1060,13 @@
 		if (gid != NULL)
 			*gid = p->auth_srv_gid;
 		if (ngids != NULL && gids != NULL) {
-			*ngids = p->auth_srv_ngids;
-			*gids = kmem_alloc(*ngids * sizeof (gid_t), KM_SLEEP);
-			bcopy(p->auth_srv_gids, *gids, *ngids * sizeof (gid_t));
+			if ((*ngids = p->auth_srv_ngids) != 0) {
+				size_t sz = *ngids * sizeof (gid_t);
+				*gids = kmem_alloc(sz, KM_SLEEP);
+				bcopy(p->auth_srv_gids, *gids, sz);
+			} else {
+				*gids = NULL;
+			}
 		}
 
 		access = p->auth_access;
--- old/usr/src/uts/common/fs/portfs/port.c	Wed Nov 20 17:16:54 2019
+++ new/usr/src/uts/common/fs/portfs/port.c	Wed Nov 20 17:16:54 2019
@@ -24,6 +24,10 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright (c) 2015 Joyent, Inc.  All rights reserved.
+ */
+
 #include <sys/types.h>
 #include <sys/systm.h>
 #include <sys/cred.h>
@@ -1379,12 +1383,18 @@
 
 	if (model == DATAMODEL_NATIVE) {
 		eventsz = sizeof (port_event_t);
-		kevp = kmem_alloc(eventsz * nmax, KM_NOSLEEP);
-		if (kevp == NULL) {
-			if (nmax > pp->port_max_list)
-				nmax = pp->port_max_list;
-			kevp = kmem_alloc(eventsz * nmax, KM_SLEEP);
+
+		if (nmax == 0) {
+			kevp = NULL;
+		} else {
+			kevp = kmem_alloc(eventsz * nmax, KM_NOSLEEP);
+			if (kevp == NULL) {
+				if (nmax > pp->port_max_list)
+					nmax = pp->port_max_list;
+				kevp = kmem_alloc(eventsz * nmax, KM_SLEEP);
+			}
 		}
+
 		results = kevp;
 		lev = NULL;	/* start with first event in the queue */
 		for (nevents = 0; nevents < nmax; ) {
@@ -1421,12 +1431,18 @@
 		port_event32_t	*kevp32;
 
 		eventsz = sizeof (port_event32_t);
-		kevp32 = kmem_alloc(eventsz * nmax, KM_NOSLEEP);
-		if (kevp32 == NULL) {
-			if (nmax > pp->port_max_list)
-				nmax = pp->port_max_list;
-			kevp32 = kmem_alloc(eventsz * nmax, KM_SLEEP);
+
+		if (nmax == 0) {
+			kevp32 = NULL;
+		} else {
+			kevp32 = kmem_alloc(eventsz * nmax, KM_NOSLEEP);
+			if (kevp32 == NULL) {
+				if (nmax > pp->port_max_list)
+					nmax = pp->port_max_list;
+				kevp32 = kmem_alloc(eventsz * nmax, KM_SLEEP);
+			}
 		}
+
 		results = kevp32;
 		lev = NULL;	/* start with first event in the queue */
 		for (nevents = 0; nevents < nmax; ) {
--- old/usr/src/uts/common/fs/zfs/sa.c	Wed Nov 20 17:16:56 2019
+++ new/usr/src/uts/common/fs/zfs/sa.c	Wed Nov 20 17:16:56 2019
@@ -405,15 +405,18 @@
 {
 	sa_os_t *sa = os->os_sa;
 	sa_lot_t *tb, *findtb;
-	int i;
+	int i, size;
 	avl_index_t loc;
 
 	ASSERT(MUTEX_HELD(&sa->sa_lock));
 	tb = kmem_zalloc(sizeof (sa_lot_t), KM_SLEEP);
 	tb->lot_attr_count = attr_count;
-	tb->lot_attrs = kmem_alloc(sizeof (sa_attr_type_t) * attr_count,
-	    KM_SLEEP);
-	bcopy(attrs, tb->lot_attrs, sizeof (sa_attr_type_t) * attr_count);
+
+	if ((size = sizeof (sa_attr_type_t) * attr_count) != 0) {
+		tb->lot_attrs = kmem_alloc(size, KM_SLEEP);
+		bcopy(attrs, tb->lot_attrs, size);
+	}
+
 	tb->lot_num = lot_num;
 	tb->lot_hash = hash;
 	tb->lot_instance = 0;
--- old/usr/src/uts/common/io/fibre-channel/impl/fctl.c	Wed Nov 20 17:16:57 2019
+++ new/usr/src/uts/common/io/fibre-channel/impl/fctl.c	Wed Nov 20 17:16:57 2019
@@ -24,6 +24,7 @@
  */
 /*
  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
+ * Copyright (c) 2015 Joyent, Inc.  All rights reserved.
  */
 /*
  * Fibre channel Transport Library (fctl)
@@ -5500,6 +5501,11 @@
 		maxPorts ++;
 	}
 
+	if (maxPorts == 0) {
+		mutex_exit(&fctl_port_lock);
+		return (0);
+	}
+
 	/* Now allocate a buffer to store all the pointers for comparisons */
 	portList = kmem_zalloc(sizeof (fc_local_port_t *) * maxPorts, KM_SLEEP);
 
--- old/usr/src/uts/common/os/fio.c	Wed Nov 20 17:17:00 2019
+++ new/usr/src/uts/common/os/fio.c	Wed Nov 20 17:17:00 2019
@@ -852,7 +852,8 @@
 	 */
 	cfip->fi_nfiles = nfiles = flist_minsize(pfip);
 
-	cfip->fi_list = kmem_zalloc(nfiles * sizeof (uf_entry_t), KM_SLEEP);
+	cfip->fi_list = nfiles == 0 ? NULL :
+	    kmem_zalloc(nfiles * sizeof (uf_entry_t), KM_SLEEP);
 
 	for (fd = 0, pufp = pfip->fi_list, cufp = cfip->fi_list; fd < nfiles;
 	    fd++, pufp++, cufp++) {
@@ -1492,8 +1493,8 @@
 fgetstartvp(int fd, char *path, vnode_t **startvpp)
 {
 	vnode_t		*startvp;
-	file_t 		*startfp;
-	char 		startchar;
+	file_t		*startfp;
+	char		startchar;
 
 	if (fd == AT_FDCWD && path == NULL)
 		return (EFAULT);
@@ -1539,7 +1540,7 @@
 {
 	vnode_t		*startvp;
 	vnode_t		*vp;
-	int 		error;
+	int		error;
 
 	/*
 	 * Since we are never called to set the size of a file, we don't
--- old/usr/src/uts/common/os/kmem.c	Wed Nov 20 17:17:02 2019
+++ new/usr/src/uts/common/os/kmem.c	Wed Nov 20 17:17:02 2019
@@ -1011,6 +1011,7 @@
 size_t kmem_content_log_size;	/* content log size [2% of memory] */
 size_t kmem_failure_log_size;	/* failure log [4 pages per CPU] */
 size_t kmem_slab_log_size;	/* slab create log [4 pages per CPU] */
+size_t kmem_zerosized_log_size;	/* zero-sized log [4 pages per CPU] */
 size_t kmem_content_maxsave = 256; /* KMF_CONTENTS max bytes to log */
 size_t kmem_lite_minsize = 0;	/* minimum buffer size for KMF_LITE */
 size_t kmem_lite_maxalign = 1024; /* maximum buffer alignment for KMF_LITE */
@@ -1018,6 +1019,14 @@
 size_t kmem_maxverify;		/* maximum bytes to inspect in debug routines */
 size_t kmem_minfirewall;	/* hardware-enforced redzone threshold */
 
+#ifdef DEBUG
+int kmem_warn_zerosized = 1;	/* whether to warn on zero-sized KM_SLEEP */
+#else
+int kmem_warn_zerosized = 0;	/* whether to warn on zero-sized KM_SLEEP */
+#endif
+
+int kmem_panic_zerosized = 0;	/* whether to panic on zero-sized KM_SLEEP */
+
 #ifdef _LP64
 size_t	kmem_max_cached = KMEM_BIG_MAXBUF;	/* maximum kmem_alloc cache */
 #else
@@ -1051,6 +1060,8 @@
 static vmem_t		*kmem_firewall_va_arena;
 static vmem_t		*kmem_firewall_arena;
 
+static int		kmem_zerosized;		/* # of zero-sized allocs */
+
 /*
  * kmem slab consolidator thresholds (tunables)
  */
@@ -1098,6 +1109,7 @@
 kmem_log_header_t	*kmem_content_log;
 kmem_log_header_t	*kmem_failure_log;
 kmem_log_header_t	*kmem_slab_log;
+kmem_log_header_t	*kmem_zerosized_log;
 
 static int		kmem_lite_count; /* # of PCs in kmem_buftag_lite_t */
 
@@ -2853,8 +2865,33 @@
 		/* fall through to kmem_cache_alloc() */
 
 	} else {
-		if (size == 0)
+		if (size == 0) {
+			if (kmflag != KM_SLEEP && !(kmflag & KM_PANIC))
+				return (NULL);
+
+			/*
+			 * If this is a sleeping allocation or one that has
+			 * been specified to panic on allocation failure, we
+			 * consider it to be deprecated behavior to allocate
+			 * 0 bytes.  If we have been configured to panic under
+			 * this condition, we panic; if to warn, we warn -- and
+			 * regardless, we log to the kmem_zerosized_log that
+			 * that this condition has occurred (which gives us
+			 * enough information to be able to debug it).
+			 */
+			if (kmem_panic && kmem_panic_zerosized)
+				panic("attempted to kmem_alloc() size of 0");
+
+			if (kmem_warn_zerosized) {
+				cmn_err(CE_WARN, "kmem_alloc(): sleeping "
+				    "allocation with size of 0; "
+				    "see kmem_zerosized_log for details");
+			}
+
+			kmem_log_event(kmem_zerosized_log, NULL, NULL, NULL);
+
 			return (NULL);
+		}
 
 		buf = vmem_alloc(kmem_oversize_arena, size,
 		    kmflag & KM_VMFLAGS);
@@ -4397,8 +4434,8 @@
 	}
 
 	kmem_failure_log = kmem_log_init(kmem_failure_log_size);
-
 	kmem_slab_log = kmem_log_init(kmem_slab_log_size);
+	kmem_zerosized_log = kmem_log_init(kmem_zerosized_log_size);
 
 	/*
 	 * Initialize STREAMS message caches so allocb() is available.
--- old/usr/src/uts/common/os/smb_subr.c	Wed Nov 20 17:17:04 2019
+++ new/usr/src/uts/common/os/smb_subr.c	Wed Nov 20 17:17:04 2019
@@ -25,7 +25,9 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (c) 2015 Joyent, Inc.  All rights reserved.
+ */
 
 #include <sys/smbios_impl.h>
 #include <sys/cmn_err.h>
@@ -43,13 +45,13 @@
 void *
 smb_alloc(size_t len)
 {
-	return (kmem_alloc(len, KM_SLEEP));
+	return (len > 0 ? kmem_alloc(len, KM_SLEEP) : NULL);
 }
 
 void *
 smb_zalloc(size_t len)
 {
-	return (kmem_zalloc(len, KM_SLEEP));
+	return (len > 0 ? kmem_zalloc(len, KM_SLEEP) : NULL);
 }
 
 void
--- old/usr/src/uts/intel/promif/prom_emul.c	Wed Nov 20 17:17:05 2019
+++ new/usr/src/uts/intel/promif/prom_emul.c	Wed Nov 20 17:17:05 2019
@@ -24,7 +24,9 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (c) 2015 Joyent, Inc.  All rights reserved.
+ */
 
 #include <sys/promif.h>
 #include <sys/promimpl.h>
@@ -46,7 +48,7 @@
 	q = kmem_zalloc(sizeof (*q), KM_SLEEP);
 	q->pp_name = kmem_zalloc(strlen(name) + 1, KM_SLEEP);
 	(void) strcpy(q->pp_name, name);
-	q->pp_val = kmem_alloc(len, KM_SLEEP);
+	q->pp_val = len > 0 ? kmem_alloc(len, KM_SLEEP) : NULL;
 	q->pp_len = len;
 	switch (flags) {
 	case DDI_PROP_TYPE_INT:
