[libgdata] core: Ensure passwords and auth. tokens are stored in non-pageable memory



commit a5a772a00526b0830c01254d0ba24305d46b1fa5
Author: Philip Withnall <philip tecnocode co uk>
Date:   Tue Oct 11 16:40:21 2011 +0100

    core: Ensure passwords and auth. tokens are stored in non-pageable memory
    
    If we're compiled with --enable-gnome, we now allocate passwords and auth.
    tokens in non-pageable memory where possible. Where not possible (due to API
    constraints imposed by ourselves or libsoup) we ensure that the details are
    zeroed out before being freed.
    
    This is all with the aim of never having passwords or auth. tokens hit disk
    or be leaked in other ways.
    
    It hasn't been formally reviewed or certified, and there are probably cases
    I've missed (which are bugs).
    
    This adds an optional dependency on libgnome-keyring when compiled with
    --enable-gnome.
    
    Helps: bgo#656783

 README                                |    1 +
 configure.ac                          |    7 +--
 gdata/gdata-client-login-authorizer.c |   48 ++++++++++++++------
 gdata/gdata-oauth1-authorizer.c       |   53 ++++++++++++++++++---
 gdata/gdata-private.h                 |    7 +++
 gdata/gdata-service.c                 |   81 +++++++++++++++++++++++++++++++++
 6 files changed, 170 insertions(+), 27 deletions(-)
---
diff --git a/README b/README
index 82893ea..b7c4b2f 100644
--- a/README
+++ b/README
@@ -21,6 +21,7 @@ Dependencies
 
 If compiling with --enable-gnome (for GNOME support):
  * libsoup-gnome-2.4
+ * gnome-keyring-1
 
 Environment variables
 =====================
diff --git a/configure.ac b/configure.ac
index bc05b07..5f743eb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -78,19 +78,16 @@ AC_SUBST(GDK_PIXBUF_LIBS)
 PKG_CHECK_MODULES([GTK], [gtk+-3.0 >= $GTK_REQS], [have_gtk=yes], [have_gtk=no])
 AM_CONDITIONAL([HAVE_GTK], [test "x$have_gtk" = "xyes"])
 
-# GNOME support, which pulls in libsoup-gnome-2.4 to provide transparent proxy support
+# GNOME support, which pulls in libsoup-gnome-2.4 to provide transparent proxy support and gnome-keyring-1 to provide non-pageable memory
 AC_MSG_CHECKING(whether to build with GNOME support)
 AC_ARG_ENABLE(gnome, AS_HELP_STRING([--enable-gnome], [Whether to enable GNOME support]),, enable_gnome=yes)
 AC_MSG_RESULT($enable_gnome)
 
 if test "x$enable_gnome" = "xyes"; then
 	AC_DEFINE(HAVE_GNOME, 1, [Defined if GNOME support is enabled])
-	PKG_CHECK_MODULES(GNOME, [libsoup-gnome-2.4])
+	PKG_CHECK_MODULES([GNOME], [libsoup-gnome-2.4 gnome-keyring-1])
 fi
 
-AC_SUBST(GNOME_CFLAGS)
-AC_SUBST(GNOME_LIBS)
-
 # Various necessary functions and headers
 AC_CHECK_FUNCS([strchr])
 AC_CHECK_FUNCS([strstr])
diff --git a/gdata/gdata-client-login-authorizer.c b/gdata/gdata-client-login-authorizer.c
index 7120b98..7f55248 100644
--- a/gdata/gdata-client-login-authorizer.c
+++ b/gdata/gdata-client-login-authorizer.c
@@ -130,7 +130,7 @@ struct _GDataClientLoginAuthorizerPrivate {
 	GStaticRecMutex mutex;
 
 	gchar *username;
-	gchar *password;
+	GDataSecureString password; /* must be allocated by _gdata_service_secure_strdup() */
 
 	/* Mapping from GDataAuthorizationDomain to string? auth_token; auth_token is NULL for domains which aren't authorised at the moment */
 	GHashTable *auth_tokens;
@@ -209,6 +209,10 @@ gdata_client_login_authorizer_class_init (GDataClientLoginAuthorizerClass *klass
 	 * then be set to the password passed to gdata_client_login_authorizer_authenticate(), and a #GObject::notify signal will be emitted. If
 	 * authentication fails, it will be set to %NULL.
 	 *
+	 * If libgdata is compiled with libgnome-keyring support, the password will be stored in non-pageable memory. However, if it is retrieved
+	 * using g_object_get() (or related functions) it will be copied to non-pageable memory and could end up being written to disk. Accessing
+	 * the password using gdata_client_login_authorizer_get_password() will not perform any copies, and so maintains privacy.
+	 *
 	 * Since: 0.9.0
 	 */
 	g_object_class_install_property (gobject_class, PROP_PASSWORD,
@@ -281,7 +285,7 @@ gdata_client_login_authorizer_init (GDataClientLoginAuthorizer *self)
 
 	/* Set up the authentication mutex */
 	g_static_rec_mutex_init (&(self->priv->mutex));
-	self->priv->auth_tokens = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, g_free);
+	self->priv->auth_tokens = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, (GDestroyNotify) _gdata_service_secure_strfree);
 
 	/* Set up the session */
 	self->priv->session = _gdata_service_build_session ();
@@ -311,7 +315,7 @@ finalize (GObject *object)
 	GDataClientLoginAuthorizerPrivate *priv = GDATA_CLIENT_LOGIN_AUTHORIZER (object)->priv;
 
 	g_free (priv->username);
-	g_free (priv->password);
+	_gdata_service_secure_strfree (priv->password);
 	g_free (priv->client_id);
 	g_hash_table_destroy (priv->auth_tokens);
 	g_static_rec_mutex_free (&(priv->mutex));
@@ -339,6 +343,7 @@ get_property (GObject *object, guint property_id, GValue *value, GParamSpec *psp
 			g_static_rec_mutex_unlock (&(priv->mutex));
 			break;
 		case PROP_PASSWORD:
+			/* NOTE: This takes a pageable copy of non-pageable memory and thus could result in the password hitting disk. */
 			g_static_rec_mutex_lock (&(priv->mutex));
 			g_value_set_string (value, priv->password);
 			g_static_rec_mutex_unlock (&(priv->mutex));
@@ -381,7 +386,7 @@ set_property (GObject *object, guint property_id, const GValue *value, GParamSpe
 static void
 process_request (GDataAuthorizer *self, GDataAuthorizationDomain *domain, SoupMessage *message)
 {
-	const gchar *auth_token;
+	GDataConstSecureString auth_token; /* privacy sensitive */
 	GDataClientLoginAuthorizerPrivate *priv = GDATA_CLIENT_LOGIN_AUTHORIZER (self)->priv;
 
 	/* If the domain's NULL, return immediately */
@@ -392,7 +397,7 @@ process_request (GDataAuthorizer *self, GDataAuthorizationDomain *domain, SoupMe
 	/* Set the authorisation header */
 	g_static_rec_mutex_lock (&(priv->mutex));
 
-	auth_token = (const gchar*) g_hash_table_lookup (priv->auth_tokens, domain);
+	auth_token = (GDataConstSecureString) g_hash_table_lookup (priv->auth_tokens, domain);
 
 	if (auth_token != NULL) {
 		/* Ensure that we're using HTTPS: if not, we shouldn't set the Authorization header or we could be revealing the auth token to
@@ -400,8 +405,12 @@ process_request (GDataAuthorizer *self, GDataAuthorizationDomain *domain, SoupMe
 		if (soup_message_get_uri (message)->scheme != SOUP_URI_SCHEME_HTTPS) {
 			g_warning ("Not authorizing a non-HTTPS message with the user's ClientLogin auth token as the connection isn't secure.");
 		} else {
+			/* Ideally, authorisation_header would be allocated in non-pageable memory. However, it's copied by
+			 * soup_message_headers_replace() immediately anyway, so there's not much point. However, we do ensure we zero it out before
+			 * freeing. */
 			gchar *authorisation_header = g_strdup_printf ("GoogleLogin auth=%s", auth_token);
 			soup_message_headers_replace (message->request_headers, "Authorization", authorisation_header);
+			memset (authorisation_header, 0, strlen (authorisation_header));
 			g_free (authorisation_header);
 		}
 	}
@@ -522,8 +531,8 @@ set_authentication_details (GDataClientLoginAuthorizer *self, const gchar *usern
 		priv->username = g_strdup (username);
 	}
 
-	g_free (priv->password);
-	priv->password = g_strdup (password);
+	_gdata_service_secure_strfree (priv->password);
+	priv->password = _gdata_service_secure_strdup (password);
 
 	g_static_rec_mutex_unlock (&(priv->mutex));
 
@@ -544,7 +553,8 @@ parse_authentication_response (GDataClientLoginAuthorizer *self, GDataAuthorizat
                                const gchar *response_body, gint length, GError **error)
 {
 	GDataClientLoginAuthorizerPrivate *priv = self->priv;
-	gchar *auth_start, *auth_end, *auth_token;
+	gchar *auth_start, *auth_end;
+	GDataSecureString auth_token; /* NOTE: auth_token must be allocated using _gdata_service_secure_strdup() and friends */
 
 	/* Parse the response */
 	auth_start = strstr (response_body, "Auth=");
@@ -558,9 +568,9 @@ parse_authentication_response (GDataClientLoginAuthorizer *self, GDataAuthorizat
 		goto protocol_error;
 	}
 
-	auth_token = g_strndup (auth_start, auth_end - auth_start);
+	auth_token = _gdata_service_secure_strndup (auth_start, auth_end - auth_start);
 	if (auth_token == NULL || strlen (auth_token) == 0) {
-		g_free (auth_token);
+		_gdata_service_secure_strfree (auth_token);
 		goto protocol_error;
 	}
 
@@ -646,7 +656,9 @@ authenticate (GDataClientLoginAuthorizer *self, GDataAuthorizationDomain *domain
 	guint status;
 	gboolean retval;
 
-	/* Prepare the request */
+	/* Prepare the request.
+	 * NOTE: At this point, our non-pageable password is copied into a pageable HTTP request structure. We can't do much about this
+	 * except note that the request is transient and so the chance of it getting paged out is low (but still positive). */
 	service_name = gdata_authorization_domain_get_service_name (domain);
 	request_body = soup_form_encode ("accountType", "HOSTED_OR_GOOGLE",
 	                                 "Email", username,
@@ -840,6 +852,10 @@ login_error:
 
 	retval = parse_authentication_response (self, domain, status, message->response_body->data, message->response_body->length, error);
 
+	/* Zero out the response body to lower the chance of it (with all the juicy passwords and auth. tokens it contains) hitting disk or getting
+	 * leaked in free memory. */
+	memset ((void*) message->response_body->data, 0, message->response_body->length);
+
 	g_object_unref (message);
 
 	return retval;
@@ -854,14 +870,14 @@ protocol_error:
 
 typedef struct {
 	gchar *username;
-	gchar *password;
+	GDataSecureString password; /* NOTE: This must be allocated in non-pageable memory using _gdata_service_secure_strdup(). */
 } AuthenticateAsyncData;
 
 static void
 authenticate_async_data_free (AuthenticateAsyncData *self)
 {
 	g_free (self->username);
-	g_free (self->password);
+	_gdata_service_secure_strfree (self->password);
 
 	g_slice_free (AuthenticateAsyncData, self);
 }
@@ -945,7 +961,7 @@ gdata_client_login_authorizer_authenticate_async (GDataClientLoginAuthorizer *se
 
 	data = g_slice_new (AuthenticateAsyncData);
 	data->username = g_strdup (username);
-	data->password = g_strdup (password);
+	data->password = _gdata_service_secure_strdup (password);
 
 	result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gdata_client_login_authorizer_authenticate_async);
 	g_simple_async_result_set_handle_cancellation (result, FALSE); /* we handle our own cancellation so we can set ::username and ::password */
@@ -1210,6 +1226,10 @@ gdata_client_login_authorizer_get_username (GDataClientLoginAuthorizer *self)
  *
  * It is not safe to call this while an authentication operation is ongoing.
  *
+ * If libgdata is compiled with libgnome-keyring support, the password will be stored in non-pageable memory. Since this function doesn't return
+ * a copy of the password, the returned value is guaranteed to not hit disk. It's advised that any copies of the password made in client programs
+ * also use non-pageable memory.
+ *
  * Return value: the password of the currently authenticated user, or %NULL
  *
  * Since: 0.9.0
diff --git a/gdata/gdata-oauth1-authorizer.c b/gdata/gdata-oauth1-authorizer.c
index bce48b7..42513a4 100644
--- a/gdata/gdata-oauth1-authorizer.c
+++ b/gdata/gdata-oauth1-authorizer.c
@@ -101,6 +101,12 @@
  *		g_free (verifier);
  *		g_free (authentication_uri);
  *		g_free (token);
+ *
+ *		/<!-- -->* Zero out the secret before freeing. *<!-- -->/
+ *		if (token_secret != NULL) {
+ *			memset (token_secret, 0, strlen (token_secret));
+ *		}
+ *
  *		g_free (token_secret);
  *	}
  *
@@ -166,7 +172,7 @@ struct _GDataOAuth1AuthorizerPrivate {
 	/* Note: This is the access token, not the request token returned by gdata_oauth1_authorizer_request_authentication_uri().
 	 * It's NULL iff the authorizer isn't authenticated. token_secret must be NULL iff token is NULL. */
 	gchar *token;
-	gchar *token_secret;
+	GDataSecureString token_secret; /* must be allocated by _gdata_service_secure_strdup() */
 
 	/* Mapping from GDataAuthorizationDomain to itself; a set of domains for which ->access_token is valid. */
 	GHashTable *authorization_domains;
@@ -312,6 +318,9 @@ finalize (GObject *object)
 		soup_uri_free (priv->proxy_uri);
 	}
 
+	g_free (priv->token);
+	_gdata_service_secure_strfree (priv->token_secret);
+
 	/* Chain up to the parent class */
 	G_OBJECT_CLASS (gdata_oauth1_authorizer_parent_class)->finalize (object);
 }
@@ -535,6 +544,9 @@ sign_message (GDataOAuth1Authorizer *self, SoupMessage *message, const gchar *to
 	/*g_debug ("Signing message using Signature Base String: â%sâ and key â%sâ using method â%sâ to give signature: â%sâ.",
 	         signature_base_string->str, secret_string->str, signature_method, signature);*/
 
+	/* Zero out the secret_string before freeing it, to reduce the chance of secrets hitting disk. */
+	memset (secret_string->str, 0, secret_string->allocated_len);
+
 	g_string_free (secret_string, TRUE);
 	g_string_free (signature_base_string, TRUE);
 
@@ -656,6 +668,15 @@ gdata_oauth1_authorizer_new_for_authorization_domains (const gchar *application_
  * <ulink type="http" url="http://tools.ietf.org/html/rfc5849#section-2.2";>Section 2.2</ulink> of the
  * <ulink type="http" url="http://tools.ietf.org/html/rfc5849";>OAuth 1.0 protocol</ulink>.
  *
+ * When freeing @token_secret, it's advisable to set it to all zeros first, to reduce the chance of the sensitive token being recoverable from the
+ * free memory pool and (accidentally) leaked by a different part of the process. This can be achieved with the following code:
+ * |[
+ *	if (token_secret != NULL) {
+ *		memset (token_secret, 0, strlen (token_secret));
+ *		g_free (token_secret);
+ *	}
+ * ]|
+ *
  * Return value: (transfer full): the URI of an authentication page for the user to use; free with g_free()
  *
  * Since: 0.9.0
@@ -787,7 +808,7 @@ gdata_oauth1_authorizer_request_authentication_uri (GDataOAuth1Authorizer *self,
 
 	/* Return the token and token secret */
 	*token = g_strdup (_token);
-	*token_secret = g_strdup (_token_secret);
+	*token_secret = g_strdup (_token_secret); /* NOTE: Ideally this would be allocated in non-pageable memory, but changing that would break API */
 
 	g_hash_table_destroy (response_details);
 
@@ -797,7 +818,7 @@ gdata_oauth1_authorizer_request_authentication_uri (GDataOAuth1Authorizer *self,
 typedef struct {
 	/* All return values */
 	gchar *token;
-	gchar *token_secret;
+	gchar *token_secret; /* NOTE: Ideally this would be allocated in non-pageable memory, but changing that would break API */
 	gchar *authentication_uri;
 } RequestAuthenticationUriAsyncData;
 
@@ -881,6 +902,15 @@ gdata_oauth1_authorizer_request_authentication_uri_async (GDataOAuth1Authorizer
  * This method can fail if the server has returned an error, but this is unlikely. If it does happen, a %GDATA_SERVICE_ERROR_PROTOCOL_ERROR will be
  * raised, @token and @token_secret will be set to %NULL and %NULL will be returned.
  *
+ * When freeing @token_secret, it's advisable to set it to all zeros first, to reduce the chance of the sensitive token being recoverable from the
+ * free memory pool and (accidentally) leaked by a different part of the process. This can be achieved with the following code:
+ * |[
+ *	if (token_secret != NULL) {
+ *		memset (token_secret, 0, strlen (token_secret));
+ *		g_free (token_secret);
+ *	}
+ * ]|
+ *
  * Return value: (transfer full): the URI of an authentication page for the user to use; free with g_free()
  *
  * Since: 0.9.0
@@ -1018,6 +1048,10 @@ gdata_oauth1_authorizer_request_authorization (GDataOAuth1Authorizer *self, cons
 	 * http://tools.ietf.org/html/rfc5849#section-2.3 for details. */
 	response_details = soup_form_decode (message->response_body->data);
 
+	/* Zero out the response body to lower the chance of it (with all the auth. tokens it contains) hitting disk or getting leaked in
+	 * free memory. */
+	memset ((void*) message->response_body->data, 0, message->response_body->length);
+
 	g_object_unref (message);
 
 	_token = g_hash_table_lookup (response_details, "oauth_token");
@@ -1038,11 +1072,14 @@ gdata_oauth1_authorizer_request_authorization (GDataOAuth1Authorizer *self, cons
 	g_free (priv->token);
 	priv->token = g_strdup (_token);
 
-	g_free (priv->token_secret);
-	priv->token_secret = g_strdup (_token_secret);
+	_gdata_service_secure_strfree (priv->token_secret);
+	priv->token_secret = _gdata_service_secure_strdup (_token_secret);
 
 	g_static_mutex_unlock (&(priv->mutex));
 
+	/* Zero out the secret token before freeing the hash table, to reduce the chance of it hitting disk later. */
+	memset ((void*) _token_secret, 0, strlen (_token_secret));
+
 	g_hash_table_destroy (response_details);
 
 	return TRUE;
@@ -1051,7 +1088,7 @@ gdata_oauth1_authorizer_request_authorization (GDataOAuth1Authorizer *self, cons
 typedef struct {
 	/* Input */
 	gchar *token;
-	gchar *token_secret;
+	GDataSecureString token_secret; /* must be allocated by _gdata_service_secure_strdup() */
 	gchar *verifier;
 } RequestAuthorizationAsyncData;
 
@@ -1059,7 +1096,7 @@ static void
 request_authorization_async_data_free (RequestAuthorizationAsyncData *data)
 {
 	g_free (data->token);
-	g_free (data->token_secret);
+	_gdata_service_secure_strfree (data->token_secret);
 	g_free (data->verifier);
 
 	g_slice_free (RequestAuthorizationAsyncData, data);
@@ -1119,7 +1156,7 @@ gdata_oauth1_authorizer_request_authorization_async (GDataOAuth1Authorizer *self
 
 	data = g_slice_new (RequestAuthorizationAsyncData);
 	data->token = g_strdup (token);
-	data->token_secret = g_strdup (token_secret);
+	data->token_secret = _gdata_service_secure_strdup (token_secret);
 	data->verifier = g_strdup (verifier);
 
 	result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gdata_oauth1_authorizer_request_authorization_async);
diff --git a/gdata/gdata-private.h b/gdata/gdata-private.h
index 95685a7..11a3796 100644
--- a/gdata/gdata-private.h
+++ b/gdata/gdata-private.h
@@ -62,6 +62,13 @@ G_GNUC_INTERNAL gchar *_gdata_service_fix_uri_scheme (const gchar *uri) G_GNUC_W
 G_GNUC_INTERNAL GDataLogLevel _gdata_service_get_log_level (void) G_GNUC_CONST;
 G_GNUC_INTERNAL SoupSession *_gdata_service_build_session (void) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
 
+typedef gchar *GDataSecureString;
+typedef const gchar *GDataConstSecureString;
+
+G_GNUC_INTERNAL GDataSecureString _gdata_service_secure_strdup (const gchar *str) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+G_GNUC_INTERNAL GDataSecureString _gdata_service_secure_strndup (const gchar *str, gsize n_bytes) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+G_GNUC_INTERNAL void _gdata_service_secure_strfree (GDataSecureString str);
+
 #include "gdata-query.h"
 G_GNUC_INTERNAL void _gdata_query_set_next_uri (GDataQuery *self, const gchar *next_uri);
 G_GNUC_INTERNAL void _gdata_query_set_previous_uri (GDataQuery *self, const gchar *previous_uri);
diff --git a/gdata/gdata-service.c b/gdata/gdata-service.c
index cd98ae6..c5ce262 100644
--- a/gdata/gdata-service.c
+++ b/gdata/gdata-service.c
@@ -46,6 +46,7 @@
 
 #ifdef HAVE_GNOME
 #include <libsoup/soup-gnome-features.h>
+#include <gnome-keyring-memory.h>
 #endif /* HAVE_GNOME */
 
 #include "gdata-service.h"
@@ -2180,3 +2181,83 @@ gdata_service_set_locale (GDataService *self, const gchar *locale)
 	self->priv->locale = g_strdup (locale);
 	g_object_notify (G_OBJECT (self), "locale");
 }
+
+/*
+ * _gdata_service_secure_strdup:
+ * @str: string (which may be in pageable memory) to be duplicated, or %NULL
+ *
+ * Duplicate a string into non-pageable memory (if libgdata has been compiled with HAVE_GNOME) or just fall back to g_strdup() (if libgdata hasn't).
+ * Passing %NULL to this function will cause %NULL to be returned.
+ *
+ * Strings allocated using this function must be freed using _gdata_service_secure_strfree().
+ *
+ * Return value: non-pageable copy of @str, or %NULL
+ * Since: 0.11.0
+ */
+GDataSecureString
+_gdata_service_secure_strdup (const gchar *str)
+{
+#ifdef HAVE_GNOME
+	return gnome_keyring_memory_strdup (str);
+#else /* if !HAVE_GNOME */
+	return g_strdup (str);
+#endif /* !HAVE_GNOME */
+}
+
+/*
+ * _gdata_service_secure_strndup:
+ * @str: string (which may be in pageable memory) to be duplicated, or %NULL
+ * @n_bytes: maximum number of bytes to copy from @str
+ *
+ * Duplicate at most @n_bytes bytes from @str into non-pageable memory. See _gdata_service_secure_strdup() for more information; this function is just
+ * a version of that with the same semantics as strndup().
+ *
+ * Return value: non-pageable copy of at most the first @n_bytes bytes of @str, or %NULL
+ * Since: 0.11.0
+ */
+GDataSecureString
+_gdata_service_secure_strndup (const gchar *str, gsize n_bytes)
+{
+#ifdef HAVE_GNOME
+	gsize str_len;
+	GDataSecureString duped_str;
+
+	if (str == NULL) {
+		return NULL;
+	}
+
+	str_len = MIN (strlen (str), n_bytes);
+	duped_str = (GDataSecureString) gnome_keyring_memory_alloc (str_len + 1);
+	strncpy (duped_str, str, str_len);
+	*(duped_str + str_len) = '\0';
+
+	return duped_str;
+#else /* if !HAVE_GNOME */
+	return g_strndup (str, n_bytes);
+#endif /* !HAVE_GNOME */
+}
+
+/*
+ * _gdata_service_secure_strfree:
+ * @str: a string to free, or %NULL
+ *
+ * Free a string which was allocated securely using _gdata_service_secure_strdup().
+ * Passing %NULL to this function is safe.
+ *
+ * Since: 0.11.0
+ */
+void
+_gdata_service_secure_strfree (GDataSecureString str)
+{
+#ifdef HAVE_GNOME
+	gnome_keyring_memory_free (str);
+#else /* if !HAVE_GNOME */
+	/* Poor man's approximation to non-pageable memory: the best we can do is ensure that we don't leak it in free memory.
+	 * This can't guarantee that it hasn't hit disk at some point, but does mean it can't hit disk in future. */
+	if (str != NULL) {
+		memset (str, 0, strlen (str));
+	}
+
+	g_free (str);
+#endif /* !HAVE_GNOME */
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]