|
| 1 | +nfsd: don't ignore the return code of svc_proc_register() |
| 2 | + |
| 3 | +jira LE-4321 |
| 4 | +cve CVE-2025-22026 |
| 5 | +Rebuild_History Non-Buildable kernel-4.18.0-553.77.1.el8_10 |
| 6 | +commit-author Jeff Layton <jlayton@kernel.org> |
| 7 | +commit 930b64ca0c511521f0abdd1d57ce52b2a6e3476b |
| 8 | +Empty-Commit: Cherry-Pick Conflicts during history rebuild. |
| 9 | +Will be included in final tarball splat. Ref for failed cherry-pick at: |
| 10 | +ciq/ciq_backports/kernel-4.18.0-553.77.1.el8_10/930b64ca.failed |
| 11 | + |
| 12 | +Currently, nfsd_proc_stat_init() ignores the return value of |
| 13 | +svc_proc_register(). If the procfile creation fails, then the kernel |
| 14 | +will WARN when it tries to remove the entry later. |
| 15 | + |
| 16 | +Fix nfsd_proc_stat_init() to return the same type of pointer as |
| 17 | +svc_proc_register(), and fix up nfsd_net_init() to check that and fail |
| 18 | +the nfsd_net construction if it occurs. |
| 19 | + |
| 20 | +svc_proc_register() can fail if the dentry can't be allocated, or if an |
| 21 | +identical dentry already exists. The second case is pretty unlikely in |
| 22 | +the nfsd_net construction codepath, so if this happens, return -ENOMEM. |
| 23 | + |
| 24 | + Reported-by: syzbot+e34ad04f27991521104c@syzkaller.appspotmail.com |
| 25 | +Closes: https://lore.kernel.org/linux-nfs/67a47501.050a0220.19061f.05f9.GAE@google.com/ |
| 26 | + Cc: stable@vger.kernel.org # v6.9 |
| 27 | + Signed-off-by: Jeff Layton <jlayton@kernel.org> |
| 28 | + Signed-off-by: Chuck Lever <chuck.lever@oracle.com> |
| 29 | +(cherry picked from commit 930b64ca0c511521f0abdd1d57ce52b2a6e3476b) |
| 30 | + Signed-off-by: Jonathan Maple <jmaple@ciq.com> |
| 31 | + |
| 32 | +# Conflicts: |
| 33 | +# fs/nfsd/nfsctl.c |
| 34 | +# fs/nfsd/stats.c |
| 35 | +# fs/nfsd/stats.h |
| 36 | +diff --cc fs/nfsd/nfsctl.c |
| 37 | +index 9bd5cf3f6d3c,ac265d6fde35..000000000000 |
| 38 | +--- a/fs/nfsd/nfsctl.c |
| 39 | ++++ b/fs/nfsd/nfsctl.c |
| 40 | +@@@ -1463,27 -2198,36 +1463,57 @@@ static __net_init int nfsd_init_net(str |
| 41 | + retval = nfsd_idmap_init(net); |
| 42 | + if (retval) |
| 43 | + goto out_idmap_error; |
| 44 | + - retval = percpu_counter_init_many(nn->counter, 0, GFP_KERNEL, |
| 45 | + - NFSD_STATS_COUNTERS_NUM); |
| 46 | + + nn->nfsd_versions = NULL; |
| 47 | + + nn->nfsd4_minorversions = NULL; |
| 48 | + + retval = nfsd_reply_cache_init(nn); |
| 49 | + if (retval) |
| 50 | +++<<<<<<< HEAD |
| 51 | + + goto out_drc_error; |
| 52 | + + nn->nfsd4_lease = 90; /* default lease time */ |
| 53 | + + nn->nfsd4_grace = 90; |
| 54 | + + nn->somebody_reclaimed = false; |
| 55 | + + nn->track_reclaim_completes = false; |
| 56 | + + nn->clverifier_counter = prandom_u32(); |
| 57 | + + nn->clientid_base = prandom_u32(); |
| 58 | + + nn->clientid_counter = nn->clientid_base + 1; |
| 59 | + + nn->s2s_cp_cl_id = nn->clientid_counter++; |
| 60 | + + |
| 61 | + + atomic_set(&nn->ntf_refcnt, 0); |
| 62 | + + init_waitqueue_head(&nn->ntf_wq); |
| 63 | + + seqlock_init(&nn->boot_lock); |
| 64 | + + |
| 65 | + + return 0; |
| 66 | + + |
| 67 | + +out_drc_error: |
| 68 | +++======= |
| 69 | ++ goto out_repcache_error; |
| 70 | ++ |
| 71 | ++ memset(&nn->nfsd_svcstats, 0, sizeof(nn->nfsd_svcstats)); |
| 72 | ++ nn->nfsd_svcstats.program = &nfsd_programs[0]; |
| 73 | ++ if (!nfsd_proc_stat_init(net)) { |
| 74 | ++ retval = -ENOMEM; |
| 75 | ++ goto out_proc_error; |
| 76 | ++ } |
| 77 | ++ |
| 78 | ++ for (i = 0; i < sizeof(nn->nfsd_versions); i++) |
| 79 | ++ nn->nfsd_versions[i] = nfsd_support_version(i); |
| 80 | ++ for (i = 0; i < sizeof(nn->nfsd4_minorversions); i++) |
| 81 | ++ nn->nfsd4_minorversions[i] = nfsd_support_version(4); |
| 82 | ++ nn->nfsd_info.mutex = &nfsd_mutex; |
| 83 | ++ nn->nfsd_serv = NULL; |
| 84 | ++ nfsd4_init_leases_net(nn); |
| 85 | ++ get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key)); |
| 86 | ++ seqlock_init(&nn->writeverf_lock); |
| 87 | ++ #if IS_ENABLED(CONFIG_NFS_LOCALIO) |
| 88 | ++ spin_lock_init(&nn->local_clients_lock); |
| 89 | ++ INIT_LIST_HEAD(&nn->local_clients); |
| 90 | ++ #endif |
| 91 | ++ return 0; |
| 92 | ++ |
| 93 | ++ out_proc_error: |
| 94 | ++ percpu_counter_destroy_many(nn->counter, NFSD_STATS_COUNTERS_NUM); |
| 95 | ++ out_repcache_error: |
| 96 | +++>>>>>>> 930b64ca0c51 (nfsd: don't ignore the return code of svc_proc_register()) |
| 97 | + nfsd_idmap_shutdown(net); |
| 98 | + out_idmap_error: |
| 99 | + nfsd_export_shutdown(net); |
| 100 | +diff --cc fs/nfsd/stats.c |
| 101 | +index 9bce3b913189,f7eaf95e20fc..000000000000 |
| 102 | +--- a/fs/nfsd/stats.c |
| 103 | ++++ b/fs/nfsd/stats.c |
| 104 | +@@@ -79,26 -71,16 +79,36 @@@ static int nfsd_proc_show(struct seq_fi |
| 105 | + return 0; |
| 106 | + } |
| 107 | + |
| 108 | +++<<<<<<< HEAD |
| 109 | + +static int nfsd_proc_open(struct inode *inode, struct file *file) |
| 110 | + +{ |
| 111 | + + return single_open(file, nfsd_proc_show, NULL); |
| 112 | +++======= |
| 113 | ++ DEFINE_PROC_SHOW_ATTRIBUTE(nfsd); |
| 114 | ++ |
| 115 | ++ struct proc_dir_entry *nfsd_proc_stat_init(struct net *net) |
| 116 | ++ { |
| 117 | ++ struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
| 118 | ++ |
| 119 | ++ return svc_proc_register(net, &nn->nfsd_svcstats, &nfsd_proc_ops); |
| 120 | +++>>>>>>> 930b64ca0c51 (nfsd: don't ignore the return code of svc_proc_register()) |
| 121 | + +} |
| 122 | + + |
| 123 | + +static const struct file_operations nfsd_proc_fops = { |
| 124 | + + .open = nfsd_proc_open, |
| 125 | + + .read = seq_read, |
| 126 | + + .llseek = seq_lseek, |
| 127 | + + .release = single_release, |
| 128 | + +}; |
| 129 | + + |
| 130 | + +void |
| 131 | + +nfsd_stat_init(void) |
| 132 | + +{ |
| 133 | + + svc_proc_register(&init_net, &nfsd_svcstats, &nfsd_proc_fops); |
| 134 | + } |
| 135 | + |
| 136 | + -void nfsd_proc_stat_shutdown(struct net *net) |
| 137 | + +void |
| 138 | + +nfsd_stat_shutdown(void) |
| 139 | + { |
| 140 | + - svc_proc_unregister(net, "nfsd"); |
| 141 | + + svc_proc_unregister(&init_net, "nfsd"); |
| 142 | + } |
| 143 | +diff --cc fs/nfsd/stats.h |
| 144 | +index b23fdac69820,e4efb0e4e56d..000000000000 |
| 145 | +--- a/fs/nfsd/stats.h |
| 146 | ++++ b/fs/nfsd/stats.h |
| 147 | +@@@ -8,37 -8,69 +8,96 @@@ |
| 148 | + #define _NFSD_STATS_H |
| 149 | + |
| 150 | + #include <uapi/linux/nfsd/stats.h> |
| 151 | + -#include <linux/percpu_counter.h> |
| 152 | + |
| 153 | +++<<<<<<< HEAD |
| 154 | +++======= |
| 155 | ++ struct proc_dir_entry *nfsd_proc_stat_init(struct net *net); |
| 156 | ++ void nfsd_proc_stat_shutdown(struct net *net); |
| 157 | ++ |
| 158 | ++ static inline void nfsd_stats_rc_hits_inc(struct nfsd_net *nn) |
| 159 | ++ { |
| 160 | ++ percpu_counter_inc(&nn->counter[NFSD_STATS_RC_HITS]); |
| 161 | ++ } |
| 162 | ++ |
| 163 | ++ static inline void nfsd_stats_rc_misses_inc(struct nfsd_net *nn) |
| 164 | ++ { |
| 165 | ++ percpu_counter_inc(&nn->counter[NFSD_STATS_RC_MISSES]); |
| 166 | ++ } |
| 167 | ++ |
| 168 | ++ static inline void nfsd_stats_rc_nocache_inc(struct nfsd_net *nn) |
| 169 | ++ { |
| 170 | ++ percpu_counter_inc(&nn->counter[NFSD_STATS_RC_NOCACHE]); |
| 171 | ++ } |
| 172 | ++ |
| 173 | ++ static inline void nfsd_stats_fh_stale_inc(struct nfsd_net *nn, |
| 174 | ++ struct svc_export *exp) |
| 175 | ++ { |
| 176 | ++ percpu_counter_inc(&nn->counter[NFSD_STATS_FH_STALE]); |
| 177 | ++ if (exp && exp->ex_stats) |
| 178 | ++ percpu_counter_inc(&exp->ex_stats->counter[EXP_STATS_FH_STALE]); |
| 179 | ++ } |
| 180 | ++ |
| 181 | ++ static inline void nfsd_stats_io_read_add(struct nfsd_net *nn, |
| 182 | ++ struct svc_export *exp, s64 amount) |
| 183 | ++ { |
| 184 | ++ percpu_counter_add(&nn->counter[NFSD_STATS_IO_READ], amount); |
| 185 | ++ if (exp && exp->ex_stats) |
| 186 | ++ percpu_counter_add(&exp->ex_stats->counter[EXP_STATS_IO_READ], amount); |
| 187 | ++ } |
| 188 | ++ |
| 189 | ++ static inline void nfsd_stats_io_write_add(struct nfsd_net *nn, |
| 190 | ++ struct svc_export *exp, s64 amount) |
| 191 | ++ { |
| 192 | ++ percpu_counter_add(&nn->counter[NFSD_STATS_IO_WRITE], amount); |
| 193 | ++ if (exp && exp->ex_stats) |
| 194 | ++ percpu_counter_add(&exp->ex_stats->counter[EXP_STATS_IO_WRITE], amount); |
| 195 | ++ } |
| 196 | ++ |
| 197 | ++ static inline void nfsd_stats_payload_misses_inc(struct nfsd_net *nn) |
| 198 | ++ { |
| 199 | ++ percpu_counter_inc(&nn->counter[NFSD_STATS_PAYLOAD_MISSES]); |
| 200 | ++ } |
| 201 | ++ |
| 202 | ++ static inline void nfsd_stats_drc_mem_usage_add(struct nfsd_net *nn, s64 amount) |
| 203 | ++ { |
| 204 | ++ percpu_counter_add(&nn->counter[NFSD_STATS_DRC_MEM_USAGE], amount); |
| 205 | ++ } |
| 206 | ++ |
| 207 | ++ static inline void nfsd_stats_drc_mem_usage_sub(struct nfsd_net *nn, s64 amount) |
| 208 | ++ { |
| 209 | ++ percpu_counter_sub(&nn->counter[NFSD_STATS_DRC_MEM_USAGE], amount); |
| 210 | ++ } |
| 211 | +++>>>>>>> 930b64ca0c51 (nfsd: don't ignore the return code of svc_proc_register()) |
| 212 | + |
| 213 | + +struct nfsd_stats { |
| 214 | + + unsigned int rchits; /* repcache hits */ |
| 215 | + + unsigned int rcmisses; /* repcache hits */ |
| 216 | + + unsigned int rcnocache; /* uncached reqs */ |
| 217 | + + unsigned int fh_stale; /* FH stale error */ |
| 218 | + + unsigned int fh_lookup; /* dentry cached */ |
| 219 | + + unsigned int fh_anon; /* anon file dentry returned */ |
| 220 | + + unsigned int fh_nocache_dir; /* filehandle not found in dcache */ |
| 221 | + + unsigned int fh_nocache_nondir; /* filehandle not found in dcache */ |
| 222 | + + unsigned int io_read; /* bytes returned to read requests */ |
| 223 | + + unsigned int io_write; /* bytes passed in write requests */ |
| 224 | + + unsigned int th_cnt; /* number of available threads */ |
| 225 | + + unsigned int th_usage[10]; /* number of ticks during which n perdeciles |
| 226 | + + * of available threads were in use */ |
| 227 | + + unsigned int th_fullcnt; /* number of times last free thread was used */ |
| 228 | + + unsigned int ra_size; /* size of ra cache */ |
| 229 | + + unsigned int ra_depth[11]; /* number of times ra entry was found that deep |
| 230 | + + * in the cache (10percentiles). [10] = not found */ |
| 231 | + #ifdef CONFIG_NFSD_V4 |
| 232 | + -static inline void nfsd_stats_wdeleg_getattr_inc(struct nfsd_net *nn) |
| 233 | + -{ |
| 234 | + - percpu_counter_inc(&nn->counter[NFSD_STATS_WDELEG_GETATTR]); |
| 235 | + -} |
| 236 | + + unsigned int nfs4_opcount[LAST_NFS4_OP + 1]; /* count of individual nfsv4 operations */ |
| 237 | + #endif |
| 238 | + + |
| 239 | + +}; |
| 240 | + + |
| 241 | + + |
| 242 | + +extern struct nfsd_stats nfsdstats; |
| 243 | + +extern struct svc_stat nfsd_svcstats; |
| 244 | + + |
| 245 | + +void nfsd_stat_init(void); |
| 246 | + +void nfsd_stat_shutdown(void); |
| 247 | + + |
| 248 | + #endif /* _NFSD_STATS_H */ |
| 249 | +* Unmerged path fs/nfsd/nfsctl.c |
| 250 | +* Unmerged path fs/nfsd/stats.c |
| 251 | +* Unmerged path fs/nfsd/stats.h |
0 commit comments