Hello Chuck Lever,
The patch 125b58c13f71: "NFSD: Convert the filecache to use
rhashtable" from Jun 28, 2022, leads to the following Smatch static
checker warning:
fs/nfsd/filecache.c:1117 nfsd_file_do_acquire()
warn: 'new' was already freed.
fs/nfsd/filecache.c
1066 static __be32
1067 nfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
1068 unsigned int may_flags, struct nfsd_file **pnf, bool open)
1069 {
1070 struct nfsd_file_lookup_key key = {
1071 .type = NFSD_FILE_KEY_FULL,
1072 .need = may_flags & NFSD_FILE_MAY_MASK,
1073 .net = SVC_NET(rqstp),
1074 .cred = current_cred(),
1075 };
1076 struct nfsd_file *nf, *new;
1077 bool retry = true;
1078 __be32 status;
1079
1080 status = fh_verify(rqstp, fhp, S_IFREG,
1081 may_flags|NFSD_MAY_OWNER_OVERRIDE);
1082 if (status != nfs_ok)
1083 return status;
1084 key.inode = d_inode(fhp->fh_dentry);
1085
1086 retry:
1087 /* Avoid allocation if the item is already in cache */
1088 nf = rhashtable_lookup_fast(&nfsd_file_rhash_tbl, &key,
1089 nfsd_file_rhash_params);
1090 if (nf)
1091 nf = nfsd_file_get(nf);
1092 if (nf)
1093 goto wait_for_construction;
1094
1095 new = nfsd_file_alloc(&key, may_flags);
1096 if (!new) {
1097 status = nfserr_jukebox;
1098 goto out_status;
1099 }
1100
1101 nf = rhashtable_lookup_get_insert_key(&nfsd_file_rhash_tbl,
1102 &key, &new->nf_rhash,
1103 nfsd_file_rhash_params);
1104 if (!nf) {
1105 nf = new;
1106 goto open_file;
1107 }
1108 nfsd_file_slab_free(&new->nf_rcu);
^^^^^^^^^^^
This frees "new".
1109 if (IS_ERR(nf)) {
1110 trace_nfsd_file_insert_err(rqstp, key.inode, may_flags, PTR_ERR(nf));
1111 nf = NULL;
1112 status = nfserr_jukebox;
1113 goto out_status;
1114 }
1115 nf = nfsd_file_get(nf);
1116 if (nf == NULL) {
--> 1117 nf = new;
^^^
Use after free
1118 goto open_file;
1119 }
1120
1121 wait_for_construction:
1122 wait_on_bit(&nf->nf_flags, NFSD_FILE_PENDING, TASK_UNINTERRUPTIBLE);
1123
1124 /* Did construction of this file fail? */
1125 if (!test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
1126 trace_nfsd_file_cons_err(rqstp, key.inode, may_flags, nf);
1127 if (!retry) {
1128 status = nfserr_jukebox;
1129 goto out;
1130 }
1131 retry = false;
1132 nfsd_file_put_noref(nf);
1133 goto retry;
1134 }
1135
1136 nfsd_file_lru_remove(nf);
1137 this_cpu_inc(nfsd_file_cache_hits);
1138
1139 if (!(may_flags & NFSD_MAY_NOT_BREAK_LEASE)) {
1140 bool write = (may_flags & NFSD_MAY_WRITE);
1141
1142 if (test_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags) ||
1143 (test_bit(NFSD_FILE_BREAK_WRITE, &nf->nf_flags) && write)) {
1144 status = nfserrno(nfsd_open_break_lease(
1145 file_inode(nf->nf_file), may_flags));
1146 if (status == nfs_ok) {
1147 clear_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags);
1148 if (write)
1149 clear_bit(NFSD_FILE_BREAK_WRITE,
1150 &nf->nf_flags);
1151 }
1152 }
1153 }
1154 out:
1155 if (status == nfs_ok) {
1156 if (open)
1157 this_cpu_inc(nfsd_file_acquisitions);
1158 *pnf = nf;
1159 } else {
1160 nfsd_file_put(nf);
1161 nf = NULL;
1162 }
1163
1164 out_status:
1165 if (open)
1166 trace_nfsd_file_acquire(rqstp, key.inode, may_flags, nf, status);
1167 return status;
1168
1169 open_file:
1170 trace_nfsd_file_alloc(nf);
1171 nf->nf_mark = nfsd_file_mark_find_or_create(nf);
1172 if (nf->nf_mark) {
1173 if (open) {
1174 status = nfsd_open_verified(rqstp, fhp, may_flags,
1175 &nf->nf_file);
1176 trace_nfsd_file_open(nf, status);
1177 } else
1178 status = nfs_ok;
1179 } else
1180 status = nfserr_jukebox;
1181 /*
1182 * If construction failed, or we raced with a call to unlink()
1183 * then unhash.
1184 */
1185 if (status != nfs_ok || key.inode->i_nlink == 0)
1186 if (nfsd_file_unhash(nf))
1187 nfsd_file_put_noref(nf);
1188 clear_bit_unlock(NFSD_FILE_PENDING, &nf->nf_flags);
1189 smp_mb__after_atomic();
1190 wake_up_bit(&nf->nf_flags, NFSD_FILE_PENDING);
1191 goto out;
1192 }
regards,
dan carpenter
> On Jul 6, 2022, at 10:33 AM, Dan Carpenter <[email protected]> wrote:
>
> Hello Chuck Lever,
>
> The patch 125b58c13f71: "NFSD: Convert the filecache to use
> rhashtable" from Jun 28, 2022, leads to the following Smatch static
> checker warning:
>
> fs/nfsd/filecache.c:1117 nfsd_file_do_acquire()
> warn: 'new' was already freed.
>
> fs/nfsd/filecache.c
> 1066 static __be32
> 1067 nfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
> 1068 unsigned int may_flags, struct nfsd_file **pnf, bool open)
> 1069 {
> 1070 struct nfsd_file_lookup_key key = {
> 1071 .type = NFSD_FILE_KEY_FULL,
> 1072 .need = may_flags & NFSD_FILE_MAY_MASK,
> 1073 .net = SVC_NET(rqstp),
> 1074 .cred = current_cred(),
> 1075 };
> 1076 struct nfsd_file *nf, *new;
> 1077 bool retry = true;
> 1078 __be32 status;
> 1079
> 1080 status = fh_verify(rqstp, fhp, S_IFREG,
> 1081 may_flags|NFSD_MAY_OWNER_OVERRIDE);
> 1082 if (status != nfs_ok)
> 1083 return status;
> 1084 key.inode = d_inode(fhp->fh_dentry);
> 1085
> 1086 retry:
> 1087 /* Avoid allocation if the item is already in cache */
> 1088 nf = rhashtable_lookup_fast(&nfsd_file_rhash_tbl, &key,
> 1089 nfsd_file_rhash_params);
> 1090 if (nf)
> 1091 nf = nfsd_file_get(nf);
> 1092 if (nf)
> 1093 goto wait_for_construction;
> 1094
> 1095 new = nfsd_file_alloc(&key, may_flags);
> 1096 if (!new) {
> 1097 status = nfserr_jukebox;
> 1098 goto out_status;
> 1099 }
> 1100
> 1101 nf = rhashtable_lookup_get_insert_key(&nfsd_file_rhash_tbl,
> 1102 &key, &new->nf_rhash,
> 1103 nfsd_file_rhash_params);
> 1104 if (!nf) {
> 1105 nf = new;
> 1106 goto open_file;
> 1107 }
> 1108 nfsd_file_slab_free(&new->nf_rcu);
> ^^^^^^^^^^^
> This frees "new".
>
> 1109 if (IS_ERR(nf)) {
> 1110 trace_nfsd_file_insert_err(rqstp, key.inode, may_flags, PTR_ERR(nf));
> 1111 nf = NULL;
> 1112 status = nfserr_jukebox;
> 1113 goto out_status;
> 1114 }
> 1115 nf = nfsd_file_get(nf);
> 1116 if (nf == NULL) {
> --> 1117 nf = new;
> ^^^
> Use after free
Ugh. I wonder why I haven't hit this already.
Thanks Dan!
> 1118 goto open_file;
> 1119 }
> 1120
> 1121 wait_for_construction:
> 1122 wait_on_bit(&nf->nf_flags, NFSD_FILE_PENDING, TASK_UNINTERRUPTIBLE);
> 1123
> 1124 /* Did construction of this file fail? */
> 1125 if (!test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
> 1126 trace_nfsd_file_cons_err(rqstp, key.inode, may_flags, nf);
> 1127 if (!retry) {
> 1128 status = nfserr_jukebox;
> 1129 goto out;
> 1130 }
> 1131 retry = false;
> 1132 nfsd_file_put_noref(nf);
> 1133 goto retry;
> 1134 }
> 1135
> 1136 nfsd_file_lru_remove(nf);
> 1137 this_cpu_inc(nfsd_file_cache_hits);
> 1138
> 1139 if (!(may_flags & NFSD_MAY_NOT_BREAK_LEASE)) {
> 1140 bool write = (may_flags & NFSD_MAY_WRITE);
> 1141
> 1142 if (test_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags) ||
> 1143 (test_bit(NFSD_FILE_BREAK_WRITE, &nf->nf_flags) && write)) {
> 1144 status = nfserrno(nfsd_open_break_lease(
> 1145 file_inode(nf->nf_file), may_flags));
> 1146 if (status == nfs_ok) {
> 1147 clear_bit(NFSD_FILE_BREAK_READ, &nf->nf_flags);
> 1148 if (write)
> 1149 clear_bit(NFSD_FILE_BREAK_WRITE,
> 1150 &nf->nf_flags);
> 1151 }
> 1152 }
> 1153 }
> 1154 out:
> 1155 if (status == nfs_ok) {
> 1156 if (open)
> 1157 this_cpu_inc(nfsd_file_acquisitions);
> 1158 *pnf = nf;
> 1159 } else {
> 1160 nfsd_file_put(nf);
> 1161 nf = NULL;
> 1162 }
> 1163
> 1164 out_status:
> 1165 if (open)
> 1166 trace_nfsd_file_acquire(rqstp, key.inode, may_flags, nf, status);
> 1167 return status;
> 1168
> 1169 open_file:
> 1170 trace_nfsd_file_alloc(nf);
> 1171 nf->nf_mark = nfsd_file_mark_find_or_create(nf);
> 1172 if (nf->nf_mark) {
> 1173 if (open) {
> 1174 status = nfsd_open_verified(rqstp, fhp, may_flags,
> 1175 &nf->nf_file);
> 1176 trace_nfsd_file_open(nf, status);
> 1177 } else
> 1178 status = nfs_ok;
> 1179 } else
> 1180 status = nfserr_jukebox;
> 1181 /*
> 1182 * If construction failed, or we raced with a call to unlink()
> 1183 * then unhash.
> 1184 */
> 1185 if (status != nfs_ok || key.inode->i_nlink == 0)
> 1186 if (nfsd_file_unhash(nf))
> 1187 nfsd_file_put_noref(nf);
> 1188 clear_bit_unlock(NFSD_FILE_PENDING, &nf->nf_flags);
> 1189 smp_mb__after_atomic();
> 1190 wake_up_bit(&nf->nf_flags, NFSD_FILE_PENDING);
> 1191 goto out;
> 1192 }
>
> regards,
> dan carpenter
--
Chuck Lever