|
@@ -119,40 +119,69 @@
|
|
|
*/
|
|
|
void final_putname(struct filename *name)
|
|
|
{
|
|
|
- __putname(name->name);
|
|
|
- kfree(name);
|
|
|
+ if (name->separate) {
|
|
|
+ __putname(name->name);
|
|
|
+ kfree(name);
|
|
|
+ } else {
|
|
|
+ __putname(name);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+#define EMBEDDED_NAME_MAX (PATH_MAX - sizeof(struct filename))
|
|
|
+
|
|
|
static struct filename *
|
|
|
getname_flags(const char __user *filename, int flags, int *empty)
|
|
|
{
|
|
|
struct filename *result, *err;
|
|
|
- char *kname;
|
|
|
int len;
|
|
|
+ long max;
|
|
|
+ char *kname;
|
|
|
|
|
|
result = audit_reusename(filename);
|
|
|
if (result)
|
|
|
return result;
|
|
|
|
|
|
- /* FIXME: create dedicated slabcache? */
|
|
|
- result = kzalloc(sizeof(*result), GFP_KERNEL);
|
|
|
+ result = __getname();
|
|
|
if (unlikely(!result))
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
- kname = __getname();
|
|
|
- if (unlikely(!kname)) {
|
|
|
- err = ERR_PTR(-ENOMEM);
|
|
|
- goto error_free_name;
|
|
|
- }
|
|
|
-
|
|
|
+ /*
|
|
|
+ * First, try to embed the struct filename inside the names_cache
|
|
|
+ * allocation
|
|
|
+ */
|
|
|
+ kname = (char *)result + sizeof(*result);
|
|
|
result->name = kname;
|
|
|
- result->uptr = filename;
|
|
|
- len = strncpy_from_user(kname, filename, PATH_MAX);
|
|
|
+ result->separate = false;
|
|
|
+ max = EMBEDDED_NAME_MAX;
|
|
|
+
|
|
|
+recopy:
|
|
|
+ len = strncpy_from_user(kname, filename, max);
|
|
|
if (unlikely(len < 0)) {
|
|
|
err = ERR_PTR(len);
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * Uh-oh. We have a name that's approaching PATH_MAX. Allocate a
|
|
|
+ * separate struct filename so we can dedicate the entire
|
|
|
+ * names_cache allocation for the pathname, and re-do the copy from
|
|
|
+ * userland.
|
|
|
+ */
|
|
|
+ if (len == EMBEDDED_NAME_MAX && max == EMBEDDED_NAME_MAX) {
|
|
|
+ kname = (char *)result;
|
|
|
+
|
|
|
+ result = kzalloc(sizeof(*result), GFP_KERNEL);
|
|
|
+ if (!result) {
|
|
|
+ err = ERR_PTR(-ENOMEM);
|
|
|
+ result = (struct filename *)kname;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ result->name = kname;
|
|
|
+ result->separate = true;
|
|
|
+ max = PATH_MAX;
|
|
|
+ goto recopy;
|
|
|
+ }
|
|
|
+
|
|
|
/* The empty path is special. */
|
|
|
if (unlikely(!len)) {
|
|
|
if (empty)
|
|
@@ -163,15 +192,15 @@ getname_flags(const char __user *filename, int flags, int *empty)
|
|
|
}
|
|
|
|
|
|
err = ERR_PTR(-ENAMETOOLONG);
|
|
|
- if (likely(len < PATH_MAX)) {
|
|
|
- audit_getname(result);
|
|
|
- return result;
|
|
|
- }
|
|
|
+ if (unlikely(len >= PATH_MAX))
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ result->uptr = filename;
|
|
|
+ audit_getname(result);
|
|
|
+ return result;
|
|
|
|
|
|
error:
|
|
|
- __putname(kname);
|
|
|
-error_free_name:
|
|
|
- kfree(result);
|
|
|
+ final_putname(result);
|
|
|
return err;
|
|
|
}
|
|
|
|