devinet_conf_proc() marks tracked per-device IPv4 settings as explicitly configured after every write attempt. A rejected integer leaves the value unchanged but sets its state bit, so a later update to conf/default skips the device. Zero-length writes and writes ignored because their file position is nonzero in strict sysctl mode have the same effect. proc_dointvec() returns success for both without parsing a value. Use proc_dointvec_conv() with a converter that records successful integer conversion. Update inheritance state and run post-set handling only after both parsing and conversion succeed. The converter uses the same integer helpers as proc_dointvec(), and valid same-value writes continue to mark a setting as explicit. The issue was reproduced with ordinary sysctl writes in fresh network namespaces, without kernel modification or error injection. On the base kernel, lo/arp_ignore remained 0 after default/arp_ignore was set to 1 following invalid, blank, zero-byte, or strict nonzero-offset writes. With this change it inherited 1 in all four cases, while valid same-value and changed-value writes retained 0 and 2. Fixes: 31be308541e9 ("[IPV4]: Add default config support after inetdev_init") Signed-off-by: Yingjie Wang --- net/ipv4/devinet.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 1e9d988849c9e..e17c82fcdefed 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -2555,14 +2555,37 @@ static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf) } } +static int devinet_conf_int_conv(bool *negp, unsigned long *u_ptr, int *k_ptr, + int dir, const struct ctl_table *ctl) +{ + int ret; + + if (dir) + ret = proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, NULL); + else + ret = proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, NULL); + + if (!ret && dir) + *(bool *)ctl->extra1 = true; + + return ret; +} + static int devinet_conf_proc(const struct ctl_table *ctl, int write, void *buffer, size_t *lenp, loff_t *ppos) { int old_value = *(int *)ctl->data; - int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); - int new_value = *(int *)ctl->data; + struct ctl_table tmp = *ctl; + bool parsed = false; + int ret, new_value; + + /* An ignored write returns success without calling the converter. */ + tmp.extra1 = &parsed; + ret = proc_dointvec_conv(&tmp, write, buffer, lenp, ppos, + devinet_conf_int_conv); + new_value = *(int *)ctl->data; - if (write) { + if (write && !ret && parsed) { struct ipv4_devconf *cnf = ctl->extra1; int i = (int *)ctl->data - cnf->data; struct net *net = ctl->extra2; -- 2.43.0