Patchwork pseudo: fix memory leak and missed privilege drop

login
register
mail settings
Submitter Peter Bigot
Date Aug. 26, 2013, 12:40 a.m.
Message ID <1377477606-17591-1-git-send-email-pab@pabigot.com>
Download mbox | patch
Permalink /patch/56589/
State Accepted
Commit 9ea32ef507c914f906b3dcc0bb29813a4e0dacba
Headers show

Comments

Peter Bigot - Aug. 26, 2013, 12:40 a.m.
From: "Peter A. Bigot" <pab@pabigot.com>

qemu.bbclass adds PSEUDO_UNLOAD=1 in qemu_run_binary to avoid reference to
pseudo functions that may not exist in the target environment.  This patch
detects the addition of that variable within the environment to which the
call applies, even if not present in the parent environment.

As a side effect it fixes a memory leak.

[YOCTO #4843]

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
---
 .../0001-pseudo_has_unload-add-function.patch      |  190 ++++++++++++++++++++
 meta/recipes-devtools/pseudo/pseudo_1.5.1.bb       |    7 +-
 2 files changed, 195 insertions(+), 2 deletions(-)
 create mode 100644 meta/recipes-devtools/pseudo/files/0001-pseudo_has_unload-add-function.patch
Peter Bigot - Aug. 26, 2013, 12:47 a.m.
On 08/25/2013 07:40 PM, Peter A. Bigot wrote:
> From: "Peter A. Bigot" <pab@pabigot.com>
>
> qemu.bbclass adds PSEUDO_UNLOAD=1 in qemu_run_binary to avoid reference to
> pseudo functions that may not exist in the target environment.  This patch
> detects the addition of that variable within the environment to which the
> call applies, even if not present in the parent environment.
>
> As a side effect it fixes a memory leak.
>
> [YOCTO #4843]

Some explanation because this one bothers me:

I ran into bug #4843 when building core-image-minimal on an Ubuntu 12.04 
host under x86_64, where the RPM post-install script for udev-hwdb ran 
udevadm under qemu-arm.  I added some debug code to pseudo and 
determined that pseudo_dropenv() was not being invoked for these 
commands, making the presence of PSEUDO_UNLOAD=1 irrelevant.

I discovered that the code that implements the unload didn't look at the 
environment that applied to the operation, only to the environment in 
which pseudo was being run.  I also noticed that the need-to-unload code 
appears to have a memory leak.

Please look this over; it does work for me, and it seems like the right 
approach, but it also seems like the approach couldn't possibly have 
worked in the past and should have been detected before so maybe there's 
something else going on my environment.

Peter
Saul Wold - Aug. 26, 2013, 4:25 a.m.
On 08/25/2013 05:40 PM, Peter A. Bigot wrote:
> From: "Peter A. Bigot" <pab@pabigot.com>
>
> qemu.bbclass adds PSEUDO_UNLOAD=1 in qemu_run_binary to avoid reference to
> pseudo functions that may not exist in the target environment.  This patch
> detects the addition of that variable within the environment to which the
> call applies, even if not present in the parent environment.
>
> As a side effect it fixes a memory leak.
>
> [YOCTO #4843]
>
> Signed-off-by: Peter A. Bigot <pab@pabigot.com>
> ---
>   .../0001-pseudo_has_unload-add-function.patch      |  190 ++++++++++++++++++++
>   meta/recipes-devtools/pseudo/pseudo_1.5.1.bb       |    7 +-
>   2 files changed, 195 insertions(+), 2 deletions(-)
>   create mode 100644 meta/recipes-devtools/pseudo/files/0001-pseudo_has_unload-add-function.patch
>
> diff --git a/meta/recipes-devtools/pseudo/files/0001-pseudo_has_unload-add-function.patch b/meta/recipes-devtools/pseudo/files/0001-pseudo_has_unload-add-function.patch
> new file mode 100644
> index 0000000..b5c81c9
> --- /dev/null
> +++ b/meta/recipes-devtools/pseudo/files/0001-pseudo_has_unload-add-function.patch
> @@ -0,0 +1,190 @@
> +From be97cb958f2934fa398fc8e344b25b84ebd4e90c Mon Sep 17 00:00:00 2001
> +From: "Peter A. Bigot" <pab@pabigot.com>
> +Date: Sun, 25 Aug 2013 19:22:09 -0500
> +Subject: [PATCH] pseudo_has_unload: add function
> +
> +Various wrappers checked for a non-null pseudo_get_value("PSEUDO_UNLOAD") to
> +determine whether the environment should include the pseudo variables.  None
> +of those checks freed the returned value when it was not null.  The new
> +check function does.
> +
> +The new check function also sees whether PSEUDO_UNLOAD was defined in the
> +environment that should be used in the wrapped system call.  This allows
> +pkg_postinst scripts to strip out the LD_PRELOAD setting, for example before
> +invoking qemu to execute commands in an environment that does not have
> +libpseudo.so.
> +
> +[YOCTO #4843]
> +
> +Upstream-Status: Pending
> +Signed-off-by: Peter A. Bigot <pab@pabigot.com>
> +---
> + ports/common/guts/execv.c              |    2 +-
> + ports/common/guts/execve.c             |    2 +-
> + ports/common/guts/execvp.c             |    2 +-
> + ports/common/guts/fork.c               |    2 +-
> + ports/linux/newclone/pseudo_wrappers.c |    2 +-
> + ports/linux/oldclone/pseudo_wrappers.c |    2 +-
> + ports/unix/guts/popen.c                |    2 +-
> + ports/unix/guts/system.c               |    2 +-
> + pseudo.h                               |    1 +
> + pseudo_util.c                          |   27 +++++++++++++++++++++++++++
> + 10 files changed, 36 insertions(+), 8 deletions(-)
> +
> +diff --git a/ports/common/guts/execv.c b/ports/common/guts/execv.c
> +index 763e1f9..3e1f820 100644
> +--- a/ports/common/guts/execv.c
> ++++ b/ports/common/guts/execv.c
> +@@ -19,7 +19,7 @@
> + 	}
> +
> + 	pseudo_setupenv();
> +-	if (pseudo_get_value("PSEUDO_UNLOAD"))
> ++	if (pseudo_has_unload(NULL))
> + 		pseudo_dropenv();
> +
> + 	/* if exec() fails, we may end up taking signals unexpectedly...
> +diff --git a/ports/common/guts/execve.c b/ports/common/guts/execve.c
> +index a003657..ff6a44e 100644
> +--- a/ports/common/guts/execve.c
> ++++ b/ports/common/guts/execve.c
> +@@ -20,7 +20,7 @@
> +         }
> +
> + 	new_environ = pseudo_setupenvp(envp);
> +-	if (pseudo_get_value("PSEUDO_UNLOAD"))
> ++	if (pseudo_has_unload(new_environ))
> + 		new_environ = pseudo_dropenvp(new_environ);
> +
> + 	/* if exec() fails, we may end up taking signals unexpectedly...
> +diff --git a/ports/common/guts/execvp.c b/ports/common/guts/execvp.c
> +index 5e75be7..04253c3 100644
> +--- a/ports/common/guts/execvp.c
> ++++ b/ports/common/guts/execvp.c
> +@@ -20,7 +20,7 @@
> +         }
> +
> + 	pseudo_setupenv();
> +-	if (pseudo_get_value("PSEUDO_UNLOAD"))
> ++	if (pseudo_has_unload(NULL))
> + 		pseudo_dropenv();
> +
> + 	/* if exec() fails, we may end up taking signals unexpectedly...
> +diff --git a/ports/common/guts/fork.c b/ports/common/guts/fork.c
> +index df8abd7..bebe3b0 100644
> +--- a/ports/common/guts/fork.c
> ++++ b/ports/common/guts/fork.c
> +@@ -12,7 +12,7 @@
> + 	 */
> + 	if (rc == 0) {
> + 		pseudo_setupenv();
> +-		if (!pseudo_get_value("PSEUDO_UNLOAD")) {
> ++		if (!pseudo_has_unload(NULL)) {
> + 			pseudo_reinit_libpseudo();
> + 		} else {
> + 			pseudo_dropenv();
> +diff --git a/ports/linux/newclone/pseudo_wrappers.c b/ports/linux/newclone/pseudo_wrappers.c
> +index 9dbac42..257e8bb 100644
> +--- a/ports/linux/newclone/pseudo_wrappers.c
> ++++ b/ports/linux/newclone/pseudo_wrappers.c
> +@@ -28,7 +28,7 @@ int wrap_clone_child(void *args) {
> +
> + 	if (!(flags & CLONE_VM)) {
> + 		pseudo_setupenv();
> +-		if (!pseudo_get_value("PSEUDO_UNLOAD")) {
> ++		if (!pseudo_has_unload(NULL)) {
> + 			pseudo_reinit_libpseudo();
> + 		} else {
> + 			pseudo_dropenv();
> +diff --git a/ports/linux/oldclone/pseudo_wrappers.c b/ports/linux/oldclone/pseudo_wrappers.c
> +index c0ce5dd..598d966 100644
> +--- a/ports/linux/oldclone/pseudo_wrappers.c
> ++++ b/ports/linux/oldclone/pseudo_wrappers.c
> +@@ -22,7 +22,7 @@ int wrap_clone_child(void *args) {
> +
> + 	if (!(flags & CLONE_VM)) {
> + 		pseudo_setupenv();
> +-		if (!pseudo_get_value("PSEUDO_UNLOAD")) {
> ++		if (!pseudo_has_unload(NULL)) {
> + 			pseudo_reinit_libpseudo();
> + 		} else {
> + 			pseudo_dropenv();
> +diff --git a/ports/unix/guts/popen.c b/ports/unix/guts/popen.c
> +index 0ca16b0..5d44c0e 100644
> +--- a/ports/unix/guts/popen.c
> ++++ b/ports/unix/guts/popen.c
> +@@ -9,7 +9,7 @@
> + 	 * in ways that avoid our usual enforcement of the environment.
> + 	 */
> + 	pseudo_setupenv();
> +-	if (pseudo_get_value("PSEUDO_UNLOAD"))
> ++	if (pseudo_has_unload(NULL))
> + 		pseudo_dropenv();
> +
> + 	rc = real_popen(command, mode);
> +diff --git a/ports/unix/guts/system.c b/ports/unix/guts/system.c
> +index 028b372..6351592 100644
> +--- a/ports/unix/guts/system.c
> ++++ b/ports/unix/guts/system.c
> +@@ -9,7 +9,7 @@
> + 		return 1;
> +
> + 	pseudo_setupenv();
> +-	if (pseudo_get_value("PSEUDO_UNLOAD"))
> ++	if (pseudo_has_unload(NULL))
> + 		pseudo_dropenv();
> +
> + 	rc = real_system(command);
> +diff --git a/pseudo.h b/pseudo.h
> +index 56760a4..f600793 100644
> +--- a/pseudo.h
> ++++ b/pseudo.h
> +@@ -28,6 +28,7 @@ extern void pseudo_init_client(void);
> + void pseudo_dump_env(char **envp);
> + int pseudo_set_value(const char *key, const char *value);
> + char *pseudo_get_value(const char *key);
> ++int pseudo_has_unload(char * const *envp);
> +
> + #include "pseudo_tables.h"
> +
> +diff --git a/pseudo_util.c b/pseudo_util.c
> +index 8d0969e..16c70e0 100644
> +--- a/pseudo_util.c
> ++++ b/pseudo_util.c
> +@@ -95,6 +95,33 @@ dump_env(char **envp) {
> + }
> + #endif
> +
> ++int
> ++pseudo_has_unload(char * const *envp) {
> ++	static const char unload[] = "PSEUDO_UNLOAD";
> ++	static size_t unload_len = strlen(unload);
> ++	size_t i = 0;
> ++
> ++	/* Is it in the caller environment? */
> ++	if (NULL != getenv(unload))
> ++		return 1;
> ++
> ++	/* Is it in the environment cache? */
> ++	if (pseudo_util_initted == -1)
> ++		pseudo_init_util();
> ++	while (pseudo_env[i].key && strcmp(pseudo_env[i].key, unload))
> ++	       ++i;
> ++	if (pseudo_env[i].key && pseudo_env[i].value)
> ++		return 1;
> ++
> ++	/* Is it in the operational environment? */
> ++	while (envp && *envp) {
> ++		if ((!strncmp(*envp, unload, unload_len)) && ('=' == (*envp)[unload_len]))
> ++			return 1;
> ++		++envp;
> ++	}
> ++	return 0;
> ++}
> ++
> + /* Caller must free memory! */
> + char *
> + pseudo_get_value(const char *key) {
> +--
> +1.7.9.5
> +
> diff --git a/meta/recipes-devtools/pseudo/pseudo_1.5.1.bb b/meta/recipes-devtools/pseudo/pseudo_1.5.1.bb
> index 5694c4d..c5df919 100644
> --- a/meta/recipes-devtools/pseudo/pseudo_1.5.1.bb
> +++ b/meta/recipes-devtools/pseudo/pseudo_1.5.1.bb
> @@ -1,8 +1,11 @@
>   require pseudo.inc
>
> -PR = "r3"
> +PR = "r4"
>
I can't speak to the pseudo patch itself, I will let Seebs way in on that!

Nut in the recipe file, we no longer do PR bumps, so this is un-needed.

Thanks for the contribution.

Sau!

> -SRC_URI = "http://www.yoctoproject.org/downloads/${BPN}/${BPN}-${PV}.tar.bz2"
> +SRC_URI = " \
> +    http://www.yoctoproject.org/downloads/${BPN}/${BPN}-${PV}.tar.bz2 \
> +    file://0001-pseudo_has_unload-add-function.patch \
> +"
>
>   SRC_URI[md5sum] = "5ec67c7bff5fe68c56de500859c19172"
>   SRC_URI[sha256sum] = "3b896f592f4d568569bd02323fad2d6b8c398e16ca36ee5a8947d2ff6c1d3d52"
>
Mark Hatle - Aug. 26, 2013, 3:54 p.m.
On 8/25/13 7:40 PM, Peter A. Bigot wrote:
> From: "Peter A. Bigot" <pab@pabigot.com>
>
> qemu.bbclass adds PSEUDO_UNLOAD=1 in qemu_run_binary to avoid reference to
> pseudo functions that may not exist in the target environment.  This patch
> detects the addition of that variable within the environment to which the
> call applies, even if not present in the parent environment.
>
> As a side effect it fixes a memory leak.

Pseudo maintainer is on vacation this week.. so I'm not sure if he'll respond or 
not.  But I'll take a whack at it.

> [YOCTO #4843]
>
> Signed-off-by: Peter A. Bigot <pab@pabigot.com>
> ---
>   .../0001-pseudo_has_unload-add-function.patch      |  190 ++++++++++++++++++++
>   meta/recipes-devtools/pseudo/pseudo_1.5.1.bb       |    7 +-
>   2 files changed, 195 insertions(+), 2 deletions(-)
>   create mode 100644 meta/recipes-devtools/pseudo/files/0001-pseudo_has_unload-add-function.patch
>
> diff --git a/meta/recipes-devtools/pseudo/files/0001-pseudo_has_unload-add-function.patch b/meta/recipes-devtools/pseudo/files/0001-pseudo_has_unload-add-function.patch
> new file mode 100644
> index 0000000..b5c81c9
> --- /dev/null
> +++ b/meta/recipes-devtools/pseudo/files/0001-pseudo_has_unload-add-function.patch
> @@ -0,0 +1,190 @@
> +From be97cb958f2934fa398fc8e344b25b84ebd4e90c Mon Sep 17 00:00:00 2001
> +From: "Peter A. Bigot" <pab@pabigot.com>
> +Date: Sun, 25 Aug 2013 19:22:09 -0500
> +Subject: [PATCH] pseudo_has_unload: add function
> +
> +Various wrappers checked for a non-null pseudo_get_value("PSEUDO_UNLOAD") to
> +determine whether the environment should include the pseudo variables.  None
> +of those checks freed the returned value when it was not null.  The new
> +check function does.
> +
> +The new check function also sees whether PSEUDO_UNLOAD was defined in the
> +environment that should be used in the wrapped system call.  This allows
> +pkg_postinst scripts to strip out the LD_PRELOAD setting, for example before
> +invoking qemu to execute commands in an environment that does not have
> +libpseudo.so.
> +
> +[YOCTO #4843]
> +
> +Upstream-Status: Pending
> +Signed-off-by: Peter A. Bigot <pab@pabigot.com>
> +---
> + ports/common/guts/execv.c              |    2 +-
> + ports/common/guts/execve.c             |    2 +-
> + ports/common/guts/execvp.c             |    2 +-
> + ports/common/guts/fork.c               |    2 +-
> + ports/linux/newclone/pseudo_wrappers.c |    2 +-
> + ports/linux/oldclone/pseudo_wrappers.c |    2 +-
> + ports/unix/guts/popen.c                |    2 +-
> + ports/unix/guts/system.c               |    2 +-
> + pseudo.h                               |    1 +
> + pseudo_util.c                          |   27 +++++++++++++++++++++++++++
> + 10 files changed, 36 insertions(+), 8 deletions(-)
> +
> +diff --git a/ports/common/guts/execv.c b/ports/common/guts/execv.c
> +index 763e1f9..3e1f820 100644
> +--- a/ports/common/guts/execv.c
> ++++ b/ports/common/guts/execv.c
> +@@ -19,7 +19,7 @@
> + 	}
> +
> + 	pseudo_setupenv();
> +-	if (pseudo_get_value("PSEUDO_UNLOAD"))
> ++	if (pseudo_has_unload(NULL))

Ya, there is definitely a memory leak here.. the pseudo_get_value says:

/* Caller must free memory! */
char *
pseudo_get_value(const char *key) {


....

> +diff --git a/pseudo_util.c b/pseudo_util.c
> +index 8d0969e..16c70e0 100644
> +--- a/pseudo_util.c
> ++++ b/pseudo_util.c
> +@@ -95,6 +95,33 @@ dump_env(char **envp) {
> + }
> + #endif
> +
> ++int
> ++pseudo_has_unload(char * const *envp) {
> ++	static const char unload[] = "PSEUDO_UNLOAD";
> ++	static size_t unload_len = strlen(unload);
> ++	size_t i = 0;
> ++
> ++	/* Is it in the caller environment? */
> ++	if (NULL != getenv(unload))
> ++		return 1;

We actually want to use the pseudo_get_value as that ensures that the internal 
environment configuration is sane at all times.  The issue is that applications 
can do odd things to the environment, and just checking the environment itself 
may not always give sane results.

The rule for variables is:

* PSEUDO Variables are loaded ASAP into a cache.  This way if the user or 
program clear or modify the environment we maintain the variable.

* To 'reset' the variables to different values an exec needs to be performed. 
(Prior to the exec we also restore any missing variables that have been cached 
to the environment.)

-however- PSEUDO_UNLOAD is supposed to be special and acted on at the vary next 
exec...  So something like the following:

int
pseudo_has_unload(char * const *envp) {
	static const char unload[] = "PSEUDO_UNLOAD";
	static size_t unload_len = strlen(unload);
	char * value = NULL;
	size_t i = 0;

	value = pseudo_get_value(unload)
	if (value) {
		free(value);
		return 1;
	}
	free(value);

	/* Is it in the caller environment? */
	if (NULL != getenv(unload))
		return 1;

	/* Is it in the operational environment? */
	while (envp && *envp) {
		if ((!strncmp(*envp, unload, unload_len)) && ('=' == (*envp)[unload_len]))
			return 1;
		++envp;
	}
	return 0;
}

This is -almost- the same as what you had.  The difference is that we check the 
cached value first, and use the get_value function which does all of the setup 
and comparison using the common function.  This ensures that the environment 
cache is properly configured as early as possible, only one accessor of the 
cache is used and it also checks the running and operation cache for the 
alternative value (in the same way your patch did it).

--Mark
Peter Bigot - Aug. 26, 2013, 4:16 p.m.
On 08/26/2013 10:54 AM, Mark Hatle wrote:
> +diff --git a/pseudo_util.c b/pseudo_util.c
>> +index 8d0969e..16c70e0 100644
>> +--- a/pseudo_util.c
>> ++++ b/pseudo_util.c
>> +@@ -95,6 +95,33 @@ dump_env(char **envp) {
>> + }
>> + #endif
>> +
>> ++int
>> ++pseudo_has_unload(char * const *envp) {
>> ++    static const char unload[] = "PSEUDO_UNLOAD";
>> ++    static size_t unload_len = strlen(unload);
>> ++    size_t i = 0;
>> ++
>> ++    /* Is it in the caller environment? */
>> ++    if (NULL != getenv(unload))
>> ++        return 1;
>
> We actually want to use the pseudo_get_value as that ensures that the 
> internal environment configuration is sane at all times.  The issue is 
> that applications can do odd things to the environment, and just 
> checking the environment itself may not always give sane results.
>
> The rule for variables is:
>
> * PSEUDO Variables are loaded ASAP into a cache.  This way if the user 
> or program clear or modify the environment we maintain the variable.
>
> * To 'reset' the variables to different values an exec needs to be 
> performed. (Prior to the exec we also restore any missing variables 
> that have been cached to the environment.)
>
> -however- PSEUDO_UNLOAD is supposed to be special and acted on at the 
> vary next exec...  So something like the following:
>
> int
> pseudo_has_unload(char * const *envp) {
>     static const char unload[] = "PSEUDO_UNLOAD";
>     static size_t unload_len = strlen(unload);
>     char * value = NULL;
>     size_t i = 0;
>
>     value = pseudo_get_value(unload)
>     if (value) {
>         free(value);
>         return 1;
>     }
>     free(value);
>
>     /* Is it in the caller environment? */
>     if (NULL != getenv(unload))
>         return 1;
>
>     /* Is it in the operational environment? */
>     while (envp && *envp) {
>         if ((!strncmp(*envp, unload, unload_len)) && ('=' == 
> (*envp)[unload_len]))
>             return 1;
>         ++envp;
>     }
>     return 0;
> }
>
> This is -almost- the same as what you had.  The difference is that we 
> check the cached value first, and use the get_value function which 
> does all of the setup and comparison using the common function.  This 
> ensures that the environment cache is properly configured as early as 
> possible, only one accessor of the cache is used and it also checks 
> the running and operation cache for the alternative value (in the same 
> way your patch did it).

Sure; I figured this was a special case, there was no point in 
allocating and immediately throwing away a value, and that since nobody 
looked at the value there was no need to update the cache through this 
call and the other cache routines would do it if necessary.  But the 
intent of the API wasn't entirely clear.  Based on what you said I'd 
probably refactor pseudo_get_value() so it and pseudo_has_unload() would 
share the code that located a matching cache entry, then duplicate the 
return value only to satisfy the API for pseudo_get_value().  Checking 
for PSEUDO_UNLOAD happens a lot in bitbaking images, so making it fast 
seems worthwhile.

I'm happy to leave this in your hands, including whether the fix 
includes a new version of pseudo or just a patch in the recipe.

Peter
Peter Seebach - Sept. 3, 2013, 3:41 p.m.
On Sun, 25 Aug 2013 19:40:06 -0500
"Peter A. Bigot" <pab@pabigot.com> wrote:

> qemu.bbclass adds PSEUDO_UNLOAD=1 in qemu_run_binary to avoid reference to
> pseudo functions that may not exist in the target environment.  This patch
> detects the addition of that variable within the environment to which the
> call applies, even if not present in the parent environment.
> 
> As a side effect it fixes a memory leak.

The memory leak was quasi-intentional -- I was aware of it, but I felt that
freeing memory right before an exec() was probably silly.

I'm currently waffling on a design question: It's obvious that, if execve()
has PSEUDO_UNLOAD=1 in envp, we want pseudo to be unloaded.

What if execve() doesn't have PSEUDO_UNLOAD=1, but some other thing has
happened which caused PSEUDO_UNLOAD to be 1 in the process that's calling
execve(), but which had not yet resulted in pseudo being unloaded? For
instance, I think there's at least one thing (memory is weak today, so I
can't remember at all where, but I think it might have been one of the Python
popen()-relatives) which lets you specify an environment, and if you do, that
is the *entire* environment -- it does not inherit.

My intuition is that if PSEUDO_UNLOAD is 1 in *either* of these, that should
win. But then we want a way to say "no, really, don't unload pseudo", which
means I might need to check for something like PSEUDO_UNLOAD=0 in envp.

So my proposed logic would be:

* if envp contains a value for PSEUDO_UNLOAD, act according to that value
* otherwise if pseudo_get_value("PSEUDO_UNLOAD") exists, use that
* otherwise act as though PSEUDO_UNLOAD is not set

(Where "is not set" implies "make sure pseudo's environment variables are
all present and expected to work.)

... But all of this is secondary. I think we should put this patch in-tree
for now so we can have the bug go away, and I can then spend a while
navel-gazing and deciding what should go into the official pseudo tree, which
is currently in a state of disrepair. Excellent catch!

-s

Patch

diff --git a/meta/recipes-devtools/pseudo/files/0001-pseudo_has_unload-add-function.patch b/meta/recipes-devtools/pseudo/files/0001-pseudo_has_unload-add-function.patch
new file mode 100644
index 0000000..b5c81c9
--- /dev/null
+++ b/meta/recipes-devtools/pseudo/files/0001-pseudo_has_unload-add-function.patch
@@ -0,0 +1,190 @@ 
+From be97cb958f2934fa398fc8e344b25b84ebd4e90c Mon Sep 17 00:00:00 2001
+From: "Peter A. Bigot" <pab@pabigot.com>
+Date: Sun, 25 Aug 2013 19:22:09 -0500
+Subject: [PATCH] pseudo_has_unload: add function
+
+Various wrappers checked for a non-null pseudo_get_value("PSEUDO_UNLOAD") to
+determine whether the environment should include the pseudo variables.  None
+of those checks freed the returned value when it was not null.  The new
+check function does.
+
+The new check function also sees whether PSEUDO_UNLOAD was defined in the
+environment that should be used in the wrapped system call.  This allows
+pkg_postinst scripts to strip out the LD_PRELOAD setting, for example before
+invoking qemu to execute commands in an environment that does not have
+libpseudo.so.
+
+[YOCTO #4843]
+
+Upstream-Status: Pending
+Signed-off-by: Peter A. Bigot <pab@pabigot.com>
+---
+ ports/common/guts/execv.c              |    2 +-
+ ports/common/guts/execve.c             |    2 +-
+ ports/common/guts/execvp.c             |    2 +-
+ ports/common/guts/fork.c               |    2 +-
+ ports/linux/newclone/pseudo_wrappers.c |    2 +-
+ ports/linux/oldclone/pseudo_wrappers.c |    2 +-
+ ports/unix/guts/popen.c                |    2 +-
+ ports/unix/guts/system.c               |    2 +-
+ pseudo.h                               |    1 +
+ pseudo_util.c                          |   27 +++++++++++++++++++++++++++
+ 10 files changed, 36 insertions(+), 8 deletions(-)
+
+diff --git a/ports/common/guts/execv.c b/ports/common/guts/execv.c
+index 763e1f9..3e1f820 100644
+--- a/ports/common/guts/execv.c
++++ b/ports/common/guts/execv.c
+@@ -19,7 +19,7 @@
+ 	}
+ 
+ 	pseudo_setupenv();
+-	if (pseudo_get_value("PSEUDO_UNLOAD"))
++	if (pseudo_has_unload(NULL))
+ 		pseudo_dropenv();
+ 
+ 	/* if exec() fails, we may end up taking signals unexpectedly...
+diff --git a/ports/common/guts/execve.c b/ports/common/guts/execve.c
+index a003657..ff6a44e 100644
+--- a/ports/common/guts/execve.c
++++ b/ports/common/guts/execve.c
+@@ -20,7 +20,7 @@
+         }
+ 
+ 	new_environ = pseudo_setupenvp(envp);
+-	if (pseudo_get_value("PSEUDO_UNLOAD"))
++	if (pseudo_has_unload(new_environ))
+ 		new_environ = pseudo_dropenvp(new_environ);
+ 
+ 	/* if exec() fails, we may end up taking signals unexpectedly...
+diff --git a/ports/common/guts/execvp.c b/ports/common/guts/execvp.c
+index 5e75be7..04253c3 100644
+--- a/ports/common/guts/execvp.c
++++ b/ports/common/guts/execvp.c
+@@ -20,7 +20,7 @@
+         }
+ 
+ 	pseudo_setupenv();
+-	if (pseudo_get_value("PSEUDO_UNLOAD"))
++	if (pseudo_has_unload(NULL))
+ 		pseudo_dropenv();
+ 
+ 	/* if exec() fails, we may end up taking signals unexpectedly...
+diff --git a/ports/common/guts/fork.c b/ports/common/guts/fork.c
+index df8abd7..bebe3b0 100644
+--- a/ports/common/guts/fork.c
++++ b/ports/common/guts/fork.c
+@@ -12,7 +12,7 @@
+ 	 */
+ 	if (rc == 0) {
+ 		pseudo_setupenv();
+-		if (!pseudo_get_value("PSEUDO_UNLOAD")) {
++		if (!pseudo_has_unload(NULL)) {
+ 			pseudo_reinit_libpseudo();
+ 		} else {
+ 			pseudo_dropenv();
+diff --git a/ports/linux/newclone/pseudo_wrappers.c b/ports/linux/newclone/pseudo_wrappers.c
+index 9dbac42..257e8bb 100644
+--- a/ports/linux/newclone/pseudo_wrappers.c
++++ b/ports/linux/newclone/pseudo_wrappers.c
+@@ -28,7 +28,7 @@ int wrap_clone_child(void *args) {
+ 
+ 	if (!(flags & CLONE_VM)) {
+ 		pseudo_setupenv();
+-		if (!pseudo_get_value("PSEUDO_UNLOAD")) {
++		if (!pseudo_has_unload(NULL)) {
+ 			pseudo_reinit_libpseudo();
+ 		} else {
+ 			pseudo_dropenv();
+diff --git a/ports/linux/oldclone/pseudo_wrappers.c b/ports/linux/oldclone/pseudo_wrappers.c
+index c0ce5dd..598d966 100644
+--- a/ports/linux/oldclone/pseudo_wrappers.c
++++ b/ports/linux/oldclone/pseudo_wrappers.c
+@@ -22,7 +22,7 @@ int wrap_clone_child(void *args) {
+ 
+ 	if (!(flags & CLONE_VM)) {
+ 		pseudo_setupenv();
+-		if (!pseudo_get_value("PSEUDO_UNLOAD")) {
++		if (!pseudo_has_unload(NULL)) {
+ 			pseudo_reinit_libpseudo();
+ 		} else {
+ 			pseudo_dropenv();
+diff --git a/ports/unix/guts/popen.c b/ports/unix/guts/popen.c
+index 0ca16b0..5d44c0e 100644
+--- a/ports/unix/guts/popen.c
++++ b/ports/unix/guts/popen.c
+@@ -9,7 +9,7 @@
+ 	 * in ways that avoid our usual enforcement of the environment.
+ 	 */
+ 	pseudo_setupenv();
+-	if (pseudo_get_value("PSEUDO_UNLOAD"))
++	if (pseudo_has_unload(NULL))
+ 		pseudo_dropenv();
+ 
+ 	rc = real_popen(command, mode);
+diff --git a/ports/unix/guts/system.c b/ports/unix/guts/system.c
+index 028b372..6351592 100644
+--- a/ports/unix/guts/system.c
++++ b/ports/unix/guts/system.c
+@@ -9,7 +9,7 @@
+ 		return 1;
+ 
+ 	pseudo_setupenv();
+-	if (pseudo_get_value("PSEUDO_UNLOAD"))
++	if (pseudo_has_unload(NULL))
+ 		pseudo_dropenv();
+ 
+ 	rc = real_system(command);
+diff --git a/pseudo.h b/pseudo.h
+index 56760a4..f600793 100644
+--- a/pseudo.h
++++ b/pseudo.h
+@@ -28,6 +28,7 @@ extern void pseudo_init_client(void);
+ void pseudo_dump_env(char **envp);
+ int pseudo_set_value(const char *key, const char *value);
+ char *pseudo_get_value(const char *key);
++int pseudo_has_unload(char * const *envp);
+ 
+ #include "pseudo_tables.h"
+ 
+diff --git a/pseudo_util.c b/pseudo_util.c
+index 8d0969e..16c70e0 100644
+--- a/pseudo_util.c
++++ b/pseudo_util.c
+@@ -95,6 +95,33 @@ dump_env(char **envp) {
+ }
+ #endif
+ 
++int
++pseudo_has_unload(char * const *envp) {
++	static const char unload[] = "PSEUDO_UNLOAD";
++	static size_t unload_len = strlen(unload);
++	size_t i = 0;
++
++	/* Is it in the caller environment? */
++	if (NULL != getenv(unload))
++		return 1;
++
++	/* Is it in the environment cache? */
++	if (pseudo_util_initted == -1)
++		pseudo_init_util();
++	while (pseudo_env[i].key && strcmp(pseudo_env[i].key, unload))
++	       ++i;
++	if (pseudo_env[i].key && pseudo_env[i].value)
++		return 1;
++
++	/* Is it in the operational environment? */
++	while (envp && *envp) {
++		if ((!strncmp(*envp, unload, unload_len)) && ('=' == (*envp)[unload_len]))
++			return 1;
++		++envp;
++	}
++	return 0;
++}
++
+ /* Caller must free memory! */
+ char *
+ pseudo_get_value(const char *key) {
+-- 
+1.7.9.5
+
diff --git a/meta/recipes-devtools/pseudo/pseudo_1.5.1.bb b/meta/recipes-devtools/pseudo/pseudo_1.5.1.bb
index 5694c4d..c5df919 100644
--- a/meta/recipes-devtools/pseudo/pseudo_1.5.1.bb
+++ b/meta/recipes-devtools/pseudo/pseudo_1.5.1.bb
@@ -1,8 +1,11 @@ 
 require pseudo.inc
 
-PR = "r3"
+PR = "r4"
 
-SRC_URI = "http://www.yoctoproject.org/downloads/${BPN}/${BPN}-${PV}.tar.bz2"
+SRC_URI = " \
+    http://www.yoctoproject.org/downloads/${BPN}/${BPN}-${PV}.tar.bz2 \
+    file://0001-pseudo_has_unload-add-function.patch \
+"
 
 SRC_URI[md5sum] = "5ec67c7bff5fe68c56de500859c19172"
 SRC_URI[sha256sum] = "3b896f592f4d568569bd02323fad2d6b8c398e16ca36ee5a8947d2ff6c1d3d52"