Bug 26034

Summary: kldload() panics if error code is returned from <module_name>_load().
Product: Base System Reporter: jtrainor <jtrainor>
Component: kernAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 4.2-RELEASE   
Hardware: Any   
OS: Any   

Description jtrainor 2001-03-23 19:00:03 UTC
panic trap 12 (page fault) in kldload while attempting to return an error from <module_name>_load.  If I set the return value prior to entering the case statement, proper behavior is observed.  If I set the return value inside the case statement, panic occurs.  See code below.

How-To-Repeat: /* =============================================================== */
/* Program..... KLD Character Device Implementation Skeleton       */
/* Version..... 1.0                                                */
/* File........ CDEVEXAM.C                                         */
/* Date........ 03/04/2001                                         */
/* Programmer.. Jim Trainor                                        */
/* Copyright (c)2001 Fastek International Ltd.                     */
/* --------------------------------------------------------------- */
/* */
/* --------------------------------------------------------------- */
/* This code is from the article "Dynamic Kernel Linker (KLD)      */
/* Facility Programming Tutorial" by Andrew Reiter.  The article   */
/* was in "daemonnews" October 2000.                               */
/* The module is loaded with "kldload ./cdev_exam.ko"              */
/* --------------------------------------------------------------- */
/* $Header$                                                        */
/* --------------------------------------------------------------- */
/* <revision comments in the form>                                 */
/* mm/dd/yyyy - pid - comment                                      */
/* =============================================================== */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/uio.h>
#include <sys/conf.h>	/* for macro DEV_MODULE */

/* --------------------------------------------------------------- */
/* Device access function declarations                             */
/* --------------------------------------------------------------- */
int example_open(dev_t dev, int oflags, int devtype, struct proc *p);
int example_close(dev_t dev, int fflag, int devtype, struct proc *p);
int example_read(dev_t dev, struct uio *uio, int ioflag);
int example_write(dev_t dev, struct uio *uio, int ioflag);

/* --------------------------------------------------------------- */
/* Define global variables.                                        */
/* --------------------------------------------------------------- */
/* Stores string recv'd by _write() */
static char buf[512+1];
static int len;

/* 
 * Used as the variable that is the reference to our device
 * in devfs... we must keep this variable sane until we 
 * call kldunload.
 */
  
static dev_t sdev;

/* --------------------------------------------------------------- */
/* File operations supported by our device driver                  */
/* --------------------------------------------------------------- */
static struct cdevsw example_cdevsw = 
{
	example_open,
	example_close,
	example_read,
	example_write,
	noioctl,
	nopoll,
	nommap,
	nostrategy,
	"example",
	34,			/* /usr/src/sys/conf/majors */		
	nodump,
	nopsize,
	D_TTY,
	-1
};

/* --------------------------------------------------------------- */
/* chardev_example_load()                                          */
/*                                                                 */
/* This is used as the function that handles what is to occur      */
/* when the KLD binary is loaded and unloaded via the kldload      */
/* and kldunload programs.                                         */
/* --------------------------------------------------------------- */
static int
chardev_example_load(struct module *m, int what, void *arg)
{
	int err = 0;
  
    /* Test load failure case 1 - before case */
/*	err = EINVAL; */

	if(!err)
	{
		switch (what) 
		{
		case MOD_LOAD:					/* kldload */
			/* Test load failure case 2 - inside case */
			err = EINVAL;
			if(!err)
			{
				sdev = make_dev(&example_cdevsw,	/* explained below */
					0,
					UID_ROOT,
					GID_WHEEL,
					0600,
					"example");
				printf("Example device loaded.\n");
			}
		break;
		case MOD_UNLOAD:
			destroy_dev(sdev);			/* explained below */
			printf("Example device unloaded.\n");
		break;
		default:
			err = EINVAL;
		break;
		}
	}
	return(err);
}

/* --------------------------------------------------------------- */
/* example_open()                                                  */
/*                                                                 */
/* This open function solely checks for open(2) flags. We are only */
/* allowing for the flags to be O_RDWR for the purpose of showing  */
/* how one could only allow a read-only device, for example.       */
/* --------------------------------------------------------------- */
int example_open(dev_t dev, int oflags, int devtype, struct proc *p)
{
	int err = 0;

	memset(&buf, '\0', 513);
	len = 0;
	uprintf("Opened device \"example\" successfully.\n");
	return(err);
}

/* --------------------------------------------------------------- */
/* example_close()                                                 */
/*                                                                 */
/* Simply "closes" our device that was opened with example_open.   */
/* --------------------------------------------------------------- */
int example_close(dev_t dev, int fflag, int devtype, struct proc *p)
{
	memset(&buf, '\0', 513);
	len = 0;
	uprintf("Closing device \"example.\"\n"); 
	return(0);
} 

/* --------------------------------------------------------------- */
/* example_write()                                                 */
/*                                                                 */
/* The read function just takes the buf that was saved             */
/* via example_write() and returns it to userland for              */
/* accessing.                                                      */
/* --------------------------------------------------------------- */
int example_read(dev_t dev, struct uio *uio, int ioflag)
{
	int err = 0;

	if (len <= 0)
	{
		err = -1; 
	}
	else
	{/* copy buf to userland */
		err = copystr(&buf, uio->uio_iov->iov_base, 513, &len);
	}
	return(err);
}

/* --------------------------------------------------------------- */
/* example_write()                                                 */
/*                                                                 */
/* example_write takes in a character string and saves it to buf   */        
/* for later accessing.                                            */
/* --------------------------------------------------------------- */
int example_write(dev_t dev, struct uio *uio, int ioflag)
{
	int err = 0;

	err = copyinstr(uio->uio_iov->iov_base, &buf, 512, &len);
	if (err != 0)
	{
		uprintf("Write to \"example\" failed.\n");
	}
	return(err);
}

DEV_MODULE(chardev_example, chardev_example_load, NULL);
Comment 1 peter 2001-03-23 21:50:52 UTC
jtrainor@fastekintl.com wrote:
> 
> >Number:         26034
> >Category:       kern
> >Synopsis:       kldload() panics if error code is returned from <module_name
    >_load().

> >Description:
> panic trap 12 (page fault) in kldload while attempting to return an error fro
    m <module_name>_load.  If I set the return value prior to entering the case
     statement, proper behavior is observed.  If I set the return value inside 
    the case statement, panic occurs.  See code below.

This is because if you fail on the MOD_LOAD case, the linker calls all the
MOD_UNLOADs for the modules in the kld file.  In your case, you
are calling "destroy_dev(sdev);" on MOD_UNLOAD, which is panicing since
sdev is uninitialized.  This is actually a bug in your module due to the
existing module load/unload semantics.  Remember, there can be more than
one module inside a .ko kld file.

One could argue that a MOD_UNLOAD event should be skipped for modules that
have failed to load successfully..  I think this change needs to happen.

Cheers,
-Peter
--
Peter Wemm - peter@FreeBSD.org; peter@yahoo-inc.com; peter@netplex.com.au
"All of this is for nothing if we don't go to the stars" - JMS/B5
Comment 2 dd freebsd_committer freebsd_triage 2001-04-29 03:27:16 UTC
State Changed
From-To: open->closed

Problem appears to be in the sample code.