rpc_read_from_socket: fix use-after-free due to missing return
[deb_libnfs.git] / lib / libnfs.c
index c2c20d4481b20945e8f30e3f8b2f28aef020a2bb..3660b6c022a17c2513628083a8c7d876aca37153 100644 (file)
@@ -422,7 +422,7 @@ static void rpc_connect_program_3_cb(struct rpc_context *rpc, int status, void *
 
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
-       if (status == RPC_STATUS_ERROR) {       
+       if (status == RPC_STATUS_ERROR) {
                data->cb(rpc, status, command_data, data->private_data);
                free_rpc_cb_data(data);
                return;
@@ -524,27 +524,16 @@ int rpc_connect_program_async(struct rpc_context *rpc, char *server, int program
        return 0;
 }
 
-void free_nfs_cb_data(struct nfs_cb_data *data)
+static void free_nfs_cb_data(struct nfs_cb_data *data)
 {
-       if (data->saved_path != NULL) {
-               free(data->saved_path);
-               data->saved_path = NULL;
-       }
-
        if (data->continue_data != NULL) {
+               assert(data->free_continue_data);
                data->free_continue_data(data->continue_data);
-               data->continue_data = NULL;
        }
 
-       if (data->fh.data.data_val != NULL) {
-               free(data->fh.data.data_val);
-               data->fh.data.data_val = NULL;
-       }
-
-       if (data->buffer != NULL) {
-               free(data->buffer);
-               data->buffer = NULL;
-       }
+       free(data->saved_path);
+       free(data->fh.data.data_val);
+       free(data->buffer);
 
        free(data);
 }
@@ -774,7 +763,7 @@ static void nfs_mount_3_cb(struct rpc_context *rpc, int status, void *command_da
 
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
-       if (status == RPC_STATUS_ERROR) {       
+       if (status == RPC_STATUS_ERROR) {
                data->cb(-EFAULT, nfs, command_data, data->private_data);
                free_nfs_cb_data(data);
                return;
@@ -999,7 +988,7 @@ static int nfs_normalize_path(struct nfs_context *nfs, char *path)
        int len;
 
        /* // -> / */
-       while (str = strstr(path, "//")) {
+       while ((str = strstr(path, "//"))) {
                while(*str) {
                        *str = *(str + 1);
                        str++;
@@ -1007,7 +996,7 @@ static int nfs_normalize_path(struct nfs_context *nfs, char *path)
        }
 
        /* /./ -> / */
-       while (str = strstr(path, "/./")) {
+       while ((str = strstr(path, "/./"))) {
                while(*(str + 1)) {
                        *str = *(str + 2);
                        str++;
@@ -1030,7 +1019,7 @@ static int nfs_normalize_path(struct nfs_context *nfs, char *path)
        }
 
        /* /string/../ -> / */
-       while (str = strstr(path, "/../")) {
+       while ((str = strstr(path, "/../"))) {
                char *tmp;
 
                if (!strncmp(path, "/../", 4)) {
@@ -1086,7 +1075,7 @@ static int nfs_normalize_path(struct nfs_context *nfs, char *path)
                        char *tmp = &path[len - 3];
                        while (*--tmp != '/')
                                ;
-                       *tmp = '\0'; 
+                       *tmp = '\0';
                }
        }
 
@@ -1105,6 +1094,8 @@ static int nfs_lookuppath_async(struct nfs_context *nfs, const char *path, nfs_c
        if (data == NULL) {
                rpc_set_error(nfs->rpc, "out of memory: failed to allocate "
                        "nfs_cb_data structure");
+               if (free_continue_data)
+                       free_continue_data(continue_data);
                return -1;
        }
        memset(data, 0, sizeof(struct nfs_cb_data));
@@ -1203,7 +1194,7 @@ static void nfs_stat_1_cb(struct rpc_context *rpc, int status, void *command_dat
 #ifndef WIN32
         st.st_blksize = 4096;
         st.st_blocks  = res->GETATTR3res_u.resok.obj_attributes.size / 4096;
-#endif//WIN32        
+#endif//WIN32
         st.st_atime   = res->GETATTR3res_u.resok.obj_attributes.atime.seconds;
         st.st_mtime   = res->GETATTR3res_u.resok.obj_attributes.mtime.seconds;
         st.st_ctime   = res->GETATTR3res_u.resok.obj_attributes.ctime.seconds;
@@ -1247,6 +1238,7 @@ static void nfs_open_trunc_cb(struct rpc_context *rpc, int status, void *command
 {
        struct nfs_cb_data *data = private_data;
        struct nfs_context *nfs = data->nfs;
+       struct nfsfh *nfsfh;
        SETATTR3res *res;
 
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
@@ -1270,7 +1262,24 @@ static void nfs_open_trunc_cb(struct rpc_context *rpc, int status, void *command
                return;
        }
 
-       data->cb(0, nfs, NULL, data->private_data);
+       nfsfh = malloc(sizeof(struct nfsfh));
+       if (nfsfh == NULL) {
+               rpc_set_error(nfs->rpc, "NFS: Failed to allocate nfsfh structure");
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       memset(nfsfh, 0, sizeof(struct nfsfh));
+
+       if (data->continue_int & O_SYNC) {
+               nfsfh->is_sync = 1;
+       }
+
+       /* steal the filehandle */
+       nfsfh->fh = data->fh;
+       data->fh.data.data_val = NULL;
+
+       data->cb(0, nfs, nfsfh, data->private_data);
        free_nfs_cb_data(data);
 }
 
@@ -1327,30 +1336,13 @@ static void nfs_open_cb(struct rpc_context *rpc, int status, void *command_data,
                return;
        }
 
-       nfsfh = malloc(sizeof(struct nfsfh));
-       if (nfsfh == NULL) {
-               rpc_set_error(nfs->rpc, "NFS: Failed to allocate nfsfh structure");
-               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
-               free_nfs_cb_data(data);
-               return;
-       }
-       memset(nfsfh, 0, sizeof(struct nfsfh));
-
-       if (data->continue_int & O_SYNC) {
-               nfsfh->is_sync = 1;
-       }
-
-       /* steal the filehandle */
-       nfsfh->fh = data->fh;
-       data->fh.data.data_val = NULL;
-
        /* Try to truncate it if we were requested to */
        if ((data->continue_int & O_TRUNC) &&
            (data->continue_int & (O_RDWR|O_WRONLY))) {
                SETATTR3args args;
 
                memset(&args, 0, sizeof(SETATTR3args));
-               args.object = nfsfh->fh;
+               args.object = data->fh;
                args.new_attributes.size.set_it = 1;
                args.new_attributes.size.set_size3_u.size = 0;
 
@@ -1366,6 +1358,23 @@ static void nfs_open_cb(struct rpc_context *rpc, int status, void *command_data,
                return;
        }
 
+       nfsfh = malloc(sizeof(struct nfsfh));
+       if (nfsfh == NULL) {
+               rpc_set_error(nfs->rpc, "NFS: Failed to allocate nfsfh structure");
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       memset(nfsfh, 0, sizeof(struct nfsfh));
+
+       if (data->continue_int & O_SYNC) {
+               nfsfh->is_sync = 1;
+       }
+
+       /* steal the filehandle */
+       nfsfh->fh = data->fh;
+       data->fh.data.data_val = NULL;
+
        data->cb(0, nfs, nfsfh, data->private_data);
        free_nfs_cb_data(data);
 }
@@ -1829,7 +1838,7 @@ int nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count
 /*
  * close
  */
+
 int nfs_close_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, void *private_data)
 {
        if (nfsfh->fh.data.data_val != NULL){
@@ -2052,7 +2061,7 @@ static void nfs_mkdir_cb(struct rpc_context *rpc, int status, void *command_data
        struct nfs_cb_data *data = private_data;
        struct nfs_context *nfs = data->nfs;
        char *str = data->continue_data;
-       
+
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
        str = &str[strlen(str) + 1];
@@ -2115,6 +2124,7 @@ int nfs_mkdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *
 
        ptr = strrchr(new_path, '/');
        if (ptr == NULL) {
+               free(new_path);
                rpc_set_error(nfs->rpc, "Invalid path %s", path);
                return -1;
        }
@@ -2142,7 +2152,7 @@ static void nfs_rmdir_cb(struct rpc_context *rpc, int status, void *command_data
        struct nfs_cb_data *data = private_data;
        struct nfs_context *nfs = data->nfs;
        char *str = data->continue_data;
-       
+
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
        str = &str[strlen(str) + 1];
@@ -2201,6 +2211,7 @@ int nfs_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *
 
        ptr = strrchr(new_path, '/');
        if (ptr == NULL) {
+               free(new_path);
                rpc_set_error(nfs->rpc, "Invalid path %s", path);
                return -1;
        }
@@ -2351,6 +2362,7 @@ int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb
        ptr = strrchr(new_path, '/');
        if (ptr == NULL) {
                rpc_set_error(nfs->rpc, "Invalid path %s", path);
+               free(new_path);
                return -1;
        }
        *ptr = 0;
@@ -2376,7 +2388,7 @@ static void nfs_unlink_cb(struct rpc_context *rpc, int status, void *command_dat
        struct nfs_cb_data *data = private_data;
        struct nfs_context *nfs = data->nfs;
        char *str = data->continue_data;
-       
+
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
        str = &str[strlen(str) + 1];
@@ -2435,6 +2447,7 @@ int nfs_unlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void
 
        ptr = strrchr(new_path, '/');
        if (ptr == NULL) {
+               free(new_path);
                rpc_set_error(nfs->rpc, "Invalid path %s", path);
                return -1;
        }
@@ -2474,7 +2487,7 @@ static void nfs_mknod_cb(struct rpc_context *rpc, int status, void *command_data
        struct nfs_cb_data *data = private_data;
        struct nfs_context *nfs = data->nfs;
        char *str = data->continue_data;
-       
+
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
        str = &str[strlen(str) + 1];
@@ -2507,7 +2520,7 @@ static int nfs_mknod_continue_internal(struct nfs_context *nfs, struct nfs_cb_da
        struct mknod_cb_data *cb_data = data->continue_data;
        char *str = cb_data->path;
        MKNOD3args args;
-       
+
        str = &str[strlen(str) + 1];
 
        args.where.dir = data->fh;
@@ -2565,13 +2578,14 @@ int nfs_mknod_async(struct nfs_context *nfs, const char *path, int mode, int dev
        cb_data->path = strdup(path);
        if (cb_data->path == NULL) {
                rpc_set_error(nfs->rpc, "Out of memory, failed to allocate mode buffer for path");
-               free(cb_data);          
+               free(cb_data);
                return -1;
        }
 
        ptr = strrchr(cb_data->path, '/');
        if (ptr == NULL) {
                rpc_set_error(nfs->rpc, "Invalid path %s", path);
+               free_mknod_cb_data(cb_data);
                return -1;
        }
        *ptr = 0;
@@ -2583,7 +2597,6 @@ int nfs_mknod_async(struct nfs_context *nfs, const char *path, int mode, int dev
        /* data->path now points to the parent directory,  and beyond the nul terminateor is the new directory to create */
        if (nfs_lookuppath_async(nfs, cb_data->path, cb, private_data, nfs_mknod_continue_internal, cb_data, free_mknod_cb_data, 0) != 0) {
                rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
-               free_mknod_cb_data(cb_data);
                return -1;
        }
 
@@ -2676,7 +2689,7 @@ static void nfs_opendir2_cb(struct rpc_context *rpc, int status, void *command_d
        struct entry3 *entry;
        uint64_t cookie = 0;
        struct rdpe_cb_data *rdpe_cb_data;
-       
+
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
        if (status == RPC_STATUS_ERROR) {
@@ -2718,6 +2731,7 @@ static void nfs_opendir2_cb(struct rpc_context *rpc, int status, void *command_d
                nfsdirent->name = strdup(entry->name);
                if (nfsdirent->name == NULL) {
                        data->cb(-ENOMEM, nfs, "Failed to allocate dirent->name", data->private_data);
+                       free(nfsdirent);
                        nfs_free_nfsdir(nfsdir);
                        data->continue_data = NULL;
                        free_nfs_cb_data(data);
@@ -2777,6 +2791,9 @@ static void nfs_opendir2_cb(struct rpc_context *rpc, int status, void *command_d
                         * commands in flight to complete
                         */
                        if (rdpe_cb_data->getattrcount > 0) {
+                               nfs_free_nfsdir(nfsdir);
+                               data->continue_data = NULL;
+                               free_nfs_cb_data(data);
                                rdpe_cb_data->status = RPC_STATUS_ERROR;
                                free(rdpe_lookup_cb_data);
                                return;
@@ -2794,7 +2811,6 @@ static void nfs_opendir2_cb(struct rpc_context *rpc, int status, void *command_d
        }
 }
 
-
 static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
 {
        READDIRPLUS3res *res = command_data;
@@ -2803,7 +2819,7 @@ static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_da
        struct nfsdir *nfsdir = data->continue_data;
        struct entryplus3 *entry;
        uint64_t cookie = 0;
-       
+
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
        if (status == RPC_STATUS_ERROR || (status == RPC_STATUS_SUCCESS && res->status == NFS3ERR_NOTSUPP) ){
@@ -2858,6 +2874,7 @@ static void nfs_opendir_cb(struct rpc_context *rpc, int status, void *command_da
                nfsdirent->name = strdup(entry->name);
                if (nfsdirent->name == NULL) {
                        data->cb(-ENOMEM, nfs, "Failed to allocate dirent->name", data->private_data);
+                       free(nfsdirent);
                        nfs_free_nfsdir(nfsdir);
                        data->continue_data = NULL;
                        free_nfs_cb_data(data);
@@ -3174,7 +3191,7 @@ static void nfs_readlink_1_cb(struct rpc_context *rpc, int status, void *command
                return;
        }
 
-       
+
        data->cb(0, nfs, res->READLINK3res_u.resok.data, data->private_data);
        free_nfs_cb_data(data);
 }
@@ -3297,7 +3314,6 @@ int nfs_fchmod_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int mode, nfs
        memcpy(data->fh.data.data_val, nfsfh->fh.data.data_val, data->fh.data.data_len);
 
        if (nfs_chmod_continue_internal(nfs, data) != 0) {
-               free_nfs_cb_data(data);
                return -1;
        }
 
@@ -3410,10 +3426,10 @@ int nfs_fchown_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int
        chown_data->uid = uid;
        chown_data->gid = gid;
 
-
        data = malloc(sizeof(struct nfs_cb_data));
        if (data == NULL) {
                rpc_set_error(nfs->rpc, "out of memory. failed to allocate memory for fchown data");
+               free(chown_data);
                return -1;
        }
        memset(data, 0, sizeof(struct nfs_cb_data));
@@ -3421,6 +3437,7 @@ int nfs_fchown_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int
        data->cb            = cb;
        data->private_data  = private_data;
        data->continue_data = chown_data;
+       data->free_continue_data = free;
        data->fh.data.data_len = nfsfh->fh.data.data_len;
        data->fh.data.data_val = malloc(data->fh.data.data_len);
        if (data->fh.data.data_val == NULL) {
@@ -3430,9 +3447,7 @@ int nfs_fchown_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int uid, int
        }
        memcpy(data->fh.data.data_val, nfsfh->fh.data.data_val, data->fh.data.data_len);
 
-
        if (nfs_chown_continue_internal(nfs, data) != 0) {
-               free_nfs_cb_data(data);
                return -1;
        }
 
@@ -3686,7 +3701,7 @@ static void nfs_symlink_cb(struct rpc_context *rpc, int status, void *command_da
        struct nfs_cb_data *data = private_data;
        struct nfs_context *nfs = data->nfs;
        struct nfs_symlink_data *symlink_data = data->continue_data;
-       
+
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
        if (status == RPC_STATUS_ERROR) {
@@ -3822,7 +3837,7 @@ static void nfs_rename_cb(struct rpc_context *rpc, int status, void *command_dat
        struct nfs_cb_data *data = private_data;
        struct nfs_context *nfs = data->nfs;
        struct nfs_rename_data *rename_data = data->continue_data;
-       
+
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
        if (status == RPC_STATUS_ERROR) {
@@ -3874,19 +3889,28 @@ static int nfs_rename_continue_2_internal(struct nfs_context *nfs, struct nfs_cb
 static int nfs_rename_continue_1_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
 {
        struct nfs_rename_data *rename_data = data->continue_data;
+       char* newpath = strdup(rename_data->newpath);
+       if (!newpath) {
+               rpc_set_error(nfs->rpc, "Out of memory. Could not allocate memory to store target path for rename");
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return -1;
+       }
 
        /* steal the filehandle */
        rename_data->olddir = data->fh;
        data->fh.data.data_val = NULL;
 
        if (nfs_lookuppath_async(nfs, rename_data->newpath, data->cb, data->private_data, nfs_rename_continue_2_internal, rename_data, free_nfs_rename_data, 0) != 0) {
-               rpc_set_error(nfs->rpc, "RPC error: Failed to send LOOKUP call for %s", rename_data->newpath);
+               rpc_set_error(nfs->rpc, "RPC error: Failed to send LOOKUP call for %s", newpath);
                data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
                free_nfs_cb_data(data);
+               free(newpath);
                return -1;
        }
        data->continue_data = NULL;
        free_nfs_cb_data(data);
+       free(newpath);
 
        return 0;
 }
@@ -3983,7 +4007,7 @@ static void nfs_link_cb(struct rpc_context *rpc, int status, void *command_data,
        struct nfs_cb_data *data = private_data;
        struct nfs_context *nfs = data->nfs;
        struct nfs_link_data *link_data = data->continue_data;
-       
+
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
        if (status == RPC_STATUS_ERROR) {
@@ -4200,7 +4224,7 @@ static void mount_export_4_cb(struct rpc_context *rpc, int status, void *command
        /* Dont want any more callbacks even if the socket is closed */
        rpc->connect_cb = NULL;
 
-       if (status == RPC_STATUS_ERROR) {       
+       if (status == RPC_STATUS_ERROR) {
                data->cb(rpc, -EFAULT, command_data, data->private_data);
                free_mount_cb_data(data);
                return;
@@ -4320,7 +4344,7 @@ int mount_getexports_async(struct rpc_context *rpc, const char *server, rpc_cb c
        if (data->server == NULL) {
                free_mount_cb_data(data);
                return -1;
-       }       
+       }
        if (rpc_connect_async(rpc, data->server, 111, mount_export_1_cb, data) != 0) {
                free_mount_cb_data(data);
                return -1;