Re: [PATCH 6/9] debugfs: Implement debugfs_create_str()

2021-03-27 Thread Steven Rostedt
On Sat, 27 Mar 2021 22:24:45 +
Al Viro  wrote:

> On Fri, Mar 26, 2021 at 11:33:58AM +0100, Peter Zijlstra wrote:
> 
> > +again:
> > +   rcu_read_lock();
> > +   str = rcu_dereference(*(char **)file->private_data);
> > +   len = strlen(str) + 1;
> > +
> > +   if (!copy || copy_len < len) {
> > +   rcu_read_unlock();
> > +   kfree(copy);
> > +   copy = kmalloc(len + 1, GFP_KERNEL);
> > +   if (!copy) {
> > +   debugfs_file_put(dentry);
> > +   return -ENOMEM;
> > +   }
> > +   copy_len = len;
> > +   goto again;
> > +   }
> > +
> > +   strncpy(copy, str, len);
> > +   copy[len] = '\n';
> > +   copy[len+1] = '\0';
> > +   rcu_read_unlock();  
> 
> *Ow*
> 
>   If the string can't change under you, what is RCU use about?
> And if it can, any use of string functions is asking for serious
> trouble; they are *not* guaranteed to be safe when any of the strings
> involved might be modified under them.

Just from looking at the above, RCU isn't protecting that the string
can change under you, but the pointer to file->private_data can.

str = rcu_dereference(*(char **)file->private_data);

That's just getting a pointer to the string. While under rcu, the value
of that string wont change nor will it be free. But file->private_data
might change, and it might free its old value, but will do so after a
RCU grace period (which is why the above has rcu_read_lock).

What the above looks like to me is a way to copy that string safely,
without worrying that it will be freed underneath you. But there's no
worry that it will change.

-- Steve


Re: [PATCH 6/9] debugfs: Implement debugfs_create_str()

2021-03-27 Thread Al Viro
On Fri, Mar 26, 2021 at 11:33:58AM +0100, Peter Zijlstra wrote:

> +again:
> + rcu_read_lock();
> + str = rcu_dereference(*(char **)file->private_data);
> + len = strlen(str) + 1;
> +
> + if (!copy || copy_len < len) {
> + rcu_read_unlock();
> + kfree(copy);
> + copy = kmalloc(len + 1, GFP_KERNEL);
> + if (!copy) {
> + debugfs_file_put(dentry);
> + return -ENOMEM;
> + }
> + copy_len = len;
> + goto again;
> + }
> +
> + strncpy(copy, str, len);
> + copy[len] = '\n';
> + copy[len+1] = '\0';
> + rcu_read_unlock();

*Ow*

If the string can't change under you, what is RCU use about?
And if it can, any use of string functions is asking for serious
trouble; they are *not* guaranteed to be safe when any of the strings
involved might be modified under them.


Re: [PATCH 6/9] debugfs: Implement debugfs_create_str()

2021-03-26 Thread Greg KH
On Fri, Mar 26, 2021 at 12:18:47PM +0100, Peter Zijlstra wrote:
> On Fri, Mar 26, 2021 at 12:05:07PM +0100, Greg KH wrote:
> > On Fri, Mar 26, 2021 at 11:33:58AM +0100, Peter Zijlstra wrote:
> > > 
> > > Signed-off-by: Peter Zijlstra (Intel) 
> > 
> > No changelog text?  :(
> 
> Yeah, didn't really know what to say that the Subject didn't already do.
> 
> > > +/**
> > > + * debugfs_create_str - create a debugfs file that is used to read and 
> > > write a string value
> > > + * @name: a pointer to a string containing the name of the file to 
> > > create.
> > > + * @mode: the permission that the file should have
> > > + * @parent: a pointer to the parent dentry for this file.  This should 
> > > be a
> > > + *  directory dentry if set.  If this parameter is %NULL, then 
> > > the
> > > + *  file will be created in the root of the debugfs filesystem.
> > > + * @value: a pointer to the variable that the file should read to and 
> > > write
> > > + * from.
> > > + *
> > > + * This function creates a file in debugfs with the given name that
> > > + * contains the value of the variable @value.  If the @mode variable is 
> > > so
> > > + * set, it can be read from, and written to.
> > > + *
> > > + * This function will return a pointer to a dentry if it succeeds.  This
> > > + * pointer must be passed to the debugfs_remove() function when the file 
> > > is
> > > + * to be removed (no automatic cleanup happens if your module is 
> > > unloaded,
> > > + * you are responsible here.)  If an error occurs, ERR_PTR(-ERROR) will 
> > > be
> > > + * returned.
> > > + *
> > > + * NOTE: when writing is enabled it will replace the string, string 
> > > lifetime is
> > > + * assumed to be RCU managed.
> > > + *
> > > + * If debugfs is not enabled in the kernel, the value ERR_PTR(-ENODEV) 
> > > will
> > > + * be returned.
> > > + */
> > > +struct dentry *debugfs_create_str(const char *name, umode_t mode,
> > > +struct dentry *parent, char **value)
> > 
> > Please have this return void, no need for me to have to clean up
> > afterward later on :)
> 
> That would make it inconsistent with debugfs_create_{bool,blob}() from
> which I copiously 'borrowed'.

As mentioned on IRC, I am trying to get rid of the return value, those
are the only 2 holdouts given their current use in some of the cruftier
areas of the kernel at the moment :(

> Also, the return dentry might be useful with debugfs_create_symlink(),
> but you're right in that most other create_file wrappers return void.

Great, change that and limit the size of the string that can be written
and it looks good to me, thanks for adding this.

greg k-h


Re: [PATCH 6/9] debugfs: Implement debugfs_create_str()

2021-03-26 Thread Peter Zijlstra
On Fri, Mar 26, 2021 at 12:05:07PM +0100, Greg KH wrote:
> On Fri, Mar 26, 2021 at 11:33:58AM +0100, Peter Zijlstra wrote:
> > 
> > Signed-off-by: Peter Zijlstra (Intel) 
> 
> No changelog text?  :(

Yeah, didn't really know what to say that the Subject didn't already do.

> > +/**
> > + * debugfs_create_str - create a debugfs file that is used to read and 
> > write a string value
> > + * @name: a pointer to a string containing the name of the file to create.
> > + * @mode: the permission that the file should have
> > + * @parent: a pointer to the parent dentry for this file.  This should be a
> > + *  directory dentry if set.  If this parameter is %NULL, then the
> > + *  file will be created in the root of the debugfs filesystem.
> > + * @value: a pointer to the variable that the file should read to and write
> > + * from.
> > + *
> > + * This function creates a file in debugfs with the given name that
> > + * contains the value of the variable @value.  If the @mode variable is so
> > + * set, it can be read from, and written to.
> > + *
> > + * This function will return a pointer to a dentry if it succeeds.  This
> > + * pointer must be passed to the debugfs_remove() function when the file is
> > + * to be removed (no automatic cleanup happens if your module is unloaded,
> > + * you are responsible here.)  If an error occurs, ERR_PTR(-ERROR) will be
> > + * returned.
> > + *
> > + * NOTE: when writing is enabled it will replace the string, string 
> > lifetime is
> > + * assumed to be RCU managed.
> > + *
> > + * If debugfs is not enabled in the kernel, the value ERR_PTR(-ENODEV) will
> > + * be returned.
> > + */
> > +struct dentry *debugfs_create_str(const char *name, umode_t mode,
> > +  struct dentry *parent, char **value)
> 
> Please have this return void, no need for me to have to clean up
> afterward later on :)

That would make it inconsistent with debugfs_create_{bool,blob}() from
which I copiously 'borrowed'.

Also, the return dentry might be useful with debugfs_create_symlink(),
but you're right in that most other create_file wrappers return void.


Re: [PATCH 6/9] debugfs: Implement debugfs_create_str()

2021-03-26 Thread Greg KH
On Fri, Mar 26, 2021 at 11:33:58AM +0100, Peter Zijlstra wrote:
> 
> Signed-off-by: Peter Zijlstra (Intel) 

No changelog text?  :(

> +/**
> + * debugfs_create_str - create a debugfs file that is used to read and write 
> a string value
> + * @name: a pointer to a string containing the name of the file to create.
> + * @mode: the permission that the file should have
> + * @parent: a pointer to the parent dentry for this file.  This should be a
> + *  directory dentry if set.  If this parameter is %NULL, then the
> + *  file will be created in the root of the debugfs filesystem.
> + * @value: a pointer to the variable that the file should read to and write
> + * from.
> + *
> + * This function creates a file in debugfs with the given name that
> + * contains the value of the variable @value.  If the @mode variable is so
> + * set, it can be read from, and written to.
> + *
> + * This function will return a pointer to a dentry if it succeeds.  This
> + * pointer must be passed to the debugfs_remove() function when the file is
> + * to be removed (no automatic cleanup happens if your module is unloaded,
> + * you are responsible here.)  If an error occurs, ERR_PTR(-ERROR) will be
> + * returned.
> + *
> + * NOTE: when writing is enabled it will replace the string, string lifetime 
> is
> + * assumed to be RCU managed.
> + *
> + * If debugfs is not enabled in the kernel, the value ERR_PTR(-ENODEV) will
> + * be returned.
> + */
> +struct dentry *debugfs_create_str(const char *name, umode_t mode,
> +struct dentry *parent, char **value)

Please have this return void, no need for me to have to clean up
afterward later on :)

thanks,

greg k-h


[PATCH 6/9] debugfs: Implement debugfs_create_str()

2021-03-26 Thread Peter Zijlstra


Signed-off-by: Peter Zijlstra (Intel) 
---
 fs/debugfs/file.c   |  144 
 include/linux/debugfs.h |   29 +
 2 files changed, 173 insertions(+)

--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -865,6 +865,150 @@ struct dentry *debugfs_create_bool(const
 }
 EXPORT_SYMBOL_GPL(debugfs_create_bool);
 
+ssize_t debugfs_read_file_str(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+   struct dentry *dentry = F_DENTRY(file);
+   char *str, *copy = NULL;
+   int copy_len, len;
+   ssize_t ret;
+
+   ret = debugfs_file_get(dentry);
+   if (unlikely(ret))
+   return ret;
+
+again:
+   rcu_read_lock();
+   str = rcu_dereference(*(char **)file->private_data);
+   len = strlen(str) + 1;
+
+   if (!copy || copy_len < len) {
+   rcu_read_unlock();
+   kfree(copy);
+   copy = kmalloc(len + 1, GFP_KERNEL);
+   if (!copy) {
+   debugfs_file_put(dentry);
+   return -ENOMEM;
+   }
+   copy_len = len;
+   goto again;
+   }
+
+   strncpy(copy, str, len);
+   copy[len] = '\n';
+   copy[len+1] = '\0';
+   rcu_read_unlock();
+
+   debugfs_file_put(dentry);
+
+   ret = simple_read_from_buffer(user_buf, count, ppos, copy, len + 1);
+   kfree(copy);
+
+   return ret;
+}
+EXPORT_SYMBOL_GPL(debugfs_read_file_str);
+
+ssize_t debugfs_write_file_str(struct file *file, const char __user *user_buf,
+  size_t count, loff_t *ppos)
+{
+   struct dentry *dentry = F_DENTRY(file);
+   char *old, *new = NULL;
+   int pos = *ppos;
+   int r;
+
+   r = debugfs_file_get(dentry);
+   if (unlikely(r))
+   return r;
+
+   old = *(char **)file->private_data;
+
+   /* only allow strict concattenation */
+   r = -EINVAL;
+   if (pos && pos != strlen(old))
+   goto error;
+
+   r = -ENOMEM;
+   new = kmalloc(pos + count + 1, GFP_KERNEL);
+   if (!new)
+   goto error;
+
+   if (pos)
+   memcpy(new, old, pos);
+
+   r = -EFAULT;
+   if (copy_from_user(new + pos, user_buf, count))
+   goto error;
+
+   new[pos + count] = '\0';
+   strim(new);
+
+   rcu_assign_pointer(*(char **)file->private_data, new);
+   synchronize_rcu();
+   kfree(old);
+
+   debugfs_file_put(dentry);
+   return count;
+
+error:
+   kfree(new);
+   debugfs_file_put(dentry);
+   return r;
+}
+EXPORT_SYMBOL_GPL(debugfs_write_file_str);
+
+static const struct file_operations fops_str = {
+   .read = debugfs_read_file_str,
+   .write =debugfs_write_file_str,
+   .open = simple_open,
+   .llseek =   default_llseek,
+};
+
+static const struct file_operations fops_str_ro = {
+   .read = debugfs_read_file_str,
+   .open = simple_open,
+   .llseek =   default_llseek,
+};
+
+static const struct file_operations fops_str_wo = {
+   .write =debugfs_write_file_str,
+   .open = simple_open,
+   .llseek =   default_llseek,
+};
+
+/**
+ * debugfs_create_str - create a debugfs file that is used to read and write a 
string value
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file.  This should be a
+ *  directory dentry if set.  If this parameter is %NULL, then the
+ *  file will be created in the root of the debugfs filesystem.
+ * @value: a pointer to the variable that the file should read to and write
+ * from.
+ *
+ * This function creates a file in debugfs with the given name that
+ * contains the value of the variable @value.  If the @mode variable is so
+ * set, it can be read from, and written to.
+ *
+ * This function will return a pointer to a dentry if it succeeds.  This
+ * pointer must be passed to the debugfs_remove() function when the file is
+ * to be removed (no automatic cleanup happens if your module is unloaded,
+ * you are responsible here.)  If an error occurs, ERR_PTR(-ERROR) will be
+ * returned.
+ *
+ * NOTE: when writing is enabled it will replace the string, string lifetime is
+ * assumed to be RCU managed.
+ *
+ * If debugfs is not enabled in the kernel, the value ERR_PTR(-ENODEV) will
+ * be returned.
+ */
+struct dentry *debugfs_create_str(const char *name, umode_t mode,
+  struct dentry *parent, char **value)
+{
+   return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_str,
+  &fops_str_ro, &fops_str_wo);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_str);
+
 static ssize_t read_file_blob(struct file *file, char __user *user_buf,