Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 8 Jun 2001 16:36:45 -0700 (PDT)
From:      Matt Dillon <dillon@earth.backplane.com>
To:        Peter Pentchev <roam@orbitel.bg>
Cc:        John Baldwin <jhb@FreeBSD.ORG>, hackers@FreeBSD.ORG
Subject:   Re: free() and const warnings
Message-ID:  <200106082336.f58Naj299417@earth.backplane.com>
References:  <20010607195634.I724@ringworld.oblivion.bg> <XFMail.010607102051.jhb@FreeBSD.org> <20010608114957.C19938@ringworld.oblivion.bg>

next in thread | previous in thread | raw e-mail | index | archive | help
:OK, here's a scenario:
:
:struct validation_fun {
:	const char	*name;
:	valfun		*fun;
:	int		dyn;
:};
:
:This is a structure for a set of validation functions, referenced by
:name from another type of object.  There are some predefined functions,
:present in the code at compile-time, and hardcoded in an array, with
:names given as "strings".  Thus, the 'const'.
:
:However, some of the functions may be defined at runtime, with both
:name and code sent by a server.  In that case, the name is a dynamically
:allocated char *, which needs to be freed upon cleanup.  So I have:
:
:[cleanup function]
:	...
:	if (val->dyn)
:		free(val->name);
:
:Any suggestions on how to improve the design to avoid this, if possible,
:would be greatly welcome.
:
:G'luck,
:Peter

    I am a great believer in const char * ... it's saved my ass on more
    occassions then I can count.  I have very similar situations where
    certain elements of a structure are usually static but sometimes dynamic.
    My recommendation is that you NOT attempt to overload the duel nature
    of 'name'.  Instead make it always a 'const char *'.

    Here is my suggestion:

struct validation_fun {
	const char	*name;
	valfun		*fun;
	int		dyn;

	/*
	 * OPAQUE
	 */
	char		*allocations;	/* or make it an array for more */
};

    As far as 99.9% of your codebase is concerned, 'name' points to a
    read-only entity and is not dynamically allocated, even from the
    point of view of FreeValidationFun().  But if you need to dynamically
    allocate the string for name you have a field to hold the dynamic
    allocation, in this case 'allocations'.  You ALSO assign the result
    to 'name', but FreeValidationFun() only looks to 'allocations' to
    determine if there is anything it must free() and, of course,
    'allocations' can remain NULL.

    This neatly solves the problem and guarentees that no confusion will
    occur.

    Here's another example:  Lets say you have a routine that can return
    an error code AND an error message, and to make it fast you want to
    be able to return static strings for the message.  But sometimes the
    error message is allocated.  So:

	char *alloc = NULL;
	const char *emsg;

	error = TheRoutine1(parameters, &emsg, &alloc);
	... process emsg ...
	error = TheRoutine2(parameters, &emsg, &alloc);
	... process emsg ...
	error = TheRoutine3(parameters, &emsg, &alloc);
	... process emsg ...
	safe_free(&alloc);

	TheRoutine*() assigns the error message simply with something like:

		*emsg = "Yah, Everything is fine";

	But if TheRoutine*() needs to allocate the error message it does
	this:

		safe_replacef(alloc, "You screwed up %d times!", n);
		*emsg = alloc;

    I have three helper routines to make this sort of string management
    trivial:

	void safe_free(char **ptr);

	    If *ptr is not NULL, free(*ptr) then set *ptr to NULL.

	void safe_replace(char **ptr, const char *str);

	    If *ptr is not NULL, free(*ptr) then set *ptr = strdup(str);

	void safe_replacef(char **ptr, const char *ctl, ...);

	    If *ptr is not NULL, free(*ptr) then use varargs and vasprintf()
	    to allocate the replacement string.

    I call these routines 'safe' because if their internal malloc()s fail
    they assert and exit... I don't like checking for memory failures
    in thousands of places in my code, I prefer to check them in just a
    handful of places.

    Probably a more involved answer then you were looking for, but hey!

    BTW, I don't use static buffers for virtually anything any more... I
    use asprintf() (or in my case, safe_asprintf()), safe_strdup(), 
    safe_replace(), safe_replacef(), safe_free(), etc...  My 'safe' routines.
    I use them for everything these days.  It removes all possibilities of a
    buffer overflow from my code.

						-Matt


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200106082336.f58Naj299417>