diff --git a/modules/user/user.module b/modules/user/user.module
index 8fad35a91b932722eceb078b5cf0cfd0adc6ec91..40023d3d12b659bc6dd04ff13897de7e639f3716 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -442,18 +442,27 @@ function user_save($account, $edit = array(), $category = 'account') {
     user_module_invoke('presave', $edit, $account, $category);
 
     // Invoke presave operations of Field Attach API and Entity API. Those APIs
-    // require a fully-fledged and updated entity object. Therefore, we need to
-    // copy any new property values of $edit into it.
+    // require a fully-fledged (and updated) entity object, so $edit is not
+    // necessarily sufficient, as it technically contains submitted form values
+    // only. Therefore, we need to clone $account into a new object and copy any
+    // new property values of $edit into it.
+    $account_updated = clone $account;
     foreach ($edit as $key => $value) {
-      $account->$key = $value;
+      $account_updated->$key = $value;
+    }
+    field_attach_presave('user', $account_updated);
+    module_invoke_all('entity_presave', $account_updated, 'user');
+    // Update $edit with any changes modules might have applied to the account.
+    foreach ($account_updated as $key => $value) {
+      if (!property_exists($account, $key) || $value !== $account->$key) {
+        $edit[$key] = $value;
+      }
     }
-    field_attach_presave('user', $account);
-    module_invoke_all('entity_presave', $account, 'user');
 
     if (is_object($account) && !$account->is_new) {
       // Process picture uploads.
-      if (!empty($account->picture->fid) && (!isset($account->original->picture->fid) || $account->picture->fid != $account->original->picture->fid)) {
-        $picture = $account->picture;
+      if (!$delete_previous_picture = empty($edit['picture']->fid)) {
+        $picture = $edit['picture'];
         // If the picture is a temporary file move it to its final location and
         // make it permanent.
         if (!$picture->status) {
@@ -466,23 +475,26 @@ function user_save($account, $edit = array(), $category = 'account') {
 
           // Move the temporary file into the final location.
           if ($picture = file_move($picture, $destination, FILE_EXISTS_RENAME)) {
+            $delete_previous_picture = TRUE;
             $picture->status = FILE_STATUS_PERMANENT;
-            $account->picture = file_save($picture);
+            $edit['picture'] = file_save($picture);
             file_usage_add($picture, 'user', 'user', $account->uid);
           }
         }
-        // Delete the previous picture if it was deleted or replaced.
-        if (!empty($account->original->picture->fid)) {
-          file_usage_delete($account->original->picture, 'user', 'user', $account->uid);
-          file_delete($account->original->picture);
-        }
       }
-      $account->picture = empty($account->picture->fid) ? 0 : $account->picture->fid;
+
+      // Delete the previous picture if it was deleted or replaced.
+      if ($delete_previous_picture && !empty($account->picture->fid)) {
+        file_usage_delete($account->picture, 'user', 'user', $account->uid);
+        file_delete($account->picture);
+      }
+
+      $edit['picture'] = empty($edit['picture']->fid) ? 0 : $edit['picture']->fid;
 
       // Do not allow 'uid' to be changed.
-      $account->uid = $account->original->uid;
+      $edit['uid'] = $account->uid;
       // Save changes to the user table.
-      $success = drupal_write_record('users', $account, 'uid');
+      $success = drupal_write_record('users', $edit, 'uid');
       if ($success === FALSE) {
         // The query failed - better to abort the save than risk further
         // data loss.
@@ -490,13 +502,13 @@ function user_save($account, $edit = array(), $category = 'account') {
       }
 
       // Reload user roles if provided.
-      if ($account->roles != $account->original->roles) {
+      if (isset($edit['roles']) && is_array($edit['roles'])) {
         db_delete('users_roles')
           ->condition('uid', $account->uid)
           ->execute();
 
         $query = db_insert('users_roles')->fields(array('uid', 'rid'));
-        foreach (array_keys($account->roles) as $rid) {
+        foreach (array_keys($edit['roles']) as $rid) {
           if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
             $query->values(array(
               'uid' => $account->uid,
@@ -508,13 +520,13 @@ function user_save($account, $edit = array(), $category = 'account') {
       }
 
       // Delete a blocked user's sessions to kick them if they are online.
-      if ($account->original->status != $account->status && $account->status == 0) {
+      if (isset($edit['status']) && $edit['status'] == 0) {
         drupal_session_destroy_uid($account->uid);
       }
 
       // If the password changed, delete all open sessions and recreate
       // the current one.
-      if ($account->pass != $account->original->pass) {
+      if (!empty($edit['pass'])) {
         drupal_session_destroy_uid($account->uid);
         if ($account->uid == $GLOBALS['user']->uid) {
           drupal_session_regenerate();
@@ -522,35 +534,37 @@ function user_save($account, $edit = array(), $category = 'account') {
       }
 
       // Save Field data.
-      field_attach_update('user', $account);
+      $entity = (object) $edit;
+      field_attach_update('user', $entity);
+
+      // Refresh user object.
+      $user = user_load($account->uid, TRUE);
+      // Make the original, unchanged user account available to update hooks.
+      if (isset($account->original)) {
+        $user->original = $account->original;
+      }
 
       // Send emails after we have the new user object.
-      if ($account->status != $account->original->status) {
+      if (isset($edit['status']) && $edit['status'] != $account->status) {
         // The user's status is changing; conditionally send notification email.
-        $op = $account->status == 1 ? 'status_activated' : 'status_blocked';
-        _user_mail_notify($op, $account);
+        $op = $edit['status'] == 1 ? 'status_activated' : 'status_blocked';
+        _user_mail_notify($op, $user);
       }
 
-      // Update $edit with any interim changes to $account.
-      foreach ($account as $key => $value) {
-        if (!property_exists($account->original, $key) || $value !== $account->original->$key) {
-          $edit[$key] = $value;
-        }
-      }
-      user_module_invoke('update', $edit, $account, $category);
-      module_invoke_all('entity_update', $account, 'user');
+      user_module_invoke('update', $edit, $user, $category);
+      module_invoke_all('entity_update', $user, 'user');
+      unset($user->original);
     }
     else {
       // Allow 'uid' to be set by the caller. There is no danger of writing an
       // existing user as drupal_write_record will do an INSERT.
-      if (empty($account->uid)) {
-        $account->uid = db_next_id(db_query('SELECT MAX(uid) FROM {users}')->fetchField());
+      if (empty($edit['uid'])) {
+        $edit['uid'] = db_next_id(db_query('SELECT MAX(uid) FROM {users}')->fetchField());
       }
       // Allow 'created' to be set by the caller.
-      if (!isset($account->created)) {
-        $account->created = REQUEST_TIME;
+      if (!isset($edit['created'])) {
+        $edit['created'] = REQUEST_TIME;
       }
-      $success = drupal_write_record('users', $account);
       $success = drupal_write_record('users', $edit);
       if ($success === FALSE) {
         // On a failed INSERT some other existing user's uid may be returned.
@@ -558,21 +572,22 @@ function user_save($account, $edit = array(), $category = 'account') {
         return FALSE;
       }
 
-      // Make sure $account is properly initialized.
-      $account->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
+      // Build a stub user object.
+      $user = (object) $edit;
+      $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
 
-      field_attach_insert('user', $account);
-      $edit = (array) $account;
-      user_module_invoke('insert', $edit, $account, $category);
-      module_invoke_all('entity_insert', $account, 'user');
+      field_attach_insert('user', $user);
+
+      user_module_invoke('insert', $edit, $user, $category);
+      module_invoke_all('entity_insert', $user, 'user');
 
       // Save user roles.
-      if (count($account->roles) > 1) {        
+      if (isset($edit['roles']) && is_array($edit['roles'])) {
         $query = db_insert('users_roles')->fields(array('uid', 'rid'));
-        foreach (array_keys($account->roles) as $rid) {
+        foreach (array_keys($edit['roles']) as $rid) {
           if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
             $query->values(array(
-              'uid' => $account->uid,
+              'uid' => $edit['uid'],
               'rid' => $rid,
             ));
           }
@@ -580,13 +595,8 @@ function user_save($account, $edit = array(), $category = 'account') {
         $query->execute();
       }
     }
-    // Clear internal properties.
-    unset($account->is_new);
-    unset($account->original);
-    // Clear the static loading cache.
-    entity_get_controller('user')->resetCache(array($account->uid));
 
-    return $account;
+    return $user;
   }
   catch (Exception $e) {
     $transaction->rollback();