@@ -930,8 +930,40 @@ int io_import_fixed(int ddir, struct iov_iter *iter,
930
930
static int io_clone_buffers (struct io_ring_ctx * ctx , struct io_ring_ctx * src_ctx ,
931
931
struct io_uring_clone_buffers * arg )
932
932
{
933
- int i , ret , nbufs , off , nr ;
934
933
struct io_rsrc_data data ;
934
+ int i , ret , off , nr ;
935
+ unsigned int nbufs ;
936
+
937
+ /* if offsets are given, must have nr specified too */
938
+ if (!arg -> nr && (arg -> dst_off || arg -> src_off ))
939
+ return - EINVAL ;
940
+ /* not allowed unless REPLACE is set */
941
+ if (ctx -> buf_table .nr && !(arg -> flags & IORING_REGISTER_DST_REPLACE ))
942
+ return - EBUSY ;
943
+
944
+ nbufs = READ_ONCE (src_ctx -> buf_table .nr );
945
+ if (!arg -> nr )
946
+ arg -> nr = nbufs ;
947
+ else if (arg -> nr > nbufs )
948
+ return - EINVAL ;
949
+ else if (arg -> nr > IORING_MAX_REG_BUFFERS )
950
+ return - EINVAL ;
951
+ if (check_add_overflow (arg -> nr , arg -> dst_off , & nbufs ))
952
+ return - EOVERFLOW ;
953
+
954
+ ret = io_rsrc_data_alloc (& data , max (nbufs , ctx -> buf_table .nr ));
955
+ if (ret )
956
+ return ret ;
957
+
958
+ /* Fill entries in data from dst that won't overlap with src */
959
+ for (i = 0 ; i < min (arg -> dst_off , ctx -> buf_table .nr ); i ++ ) {
960
+ struct io_rsrc_node * src_node = ctx -> buf_table .nodes [i ];
961
+
962
+ if (src_node ) {
963
+ data .nodes [i ] = src_node ;
964
+ src_node -> refs ++ ;
965
+ }
966
+ }
935
967
936
968
/*
937
969
* Drop our own lock here. We'll setup the data we need and reference
@@ -954,14 +986,6 @@ static int io_clone_buffers(struct io_ring_ctx *ctx, struct io_ring_ctx *src_ctx
954
986
goto out_unlock ;
955
987
if (off > nbufs )
956
988
goto out_unlock ;
957
- if (check_add_overflow (arg -> nr , arg -> dst_off , & off ))
958
- goto out_unlock ;
959
- ret = - EINVAL ;
960
- if (off > IORING_MAX_REG_BUFFERS )
961
- goto out_unlock ;
962
- ret = io_rsrc_data_alloc (& data , off );
963
- if (ret )
964
- goto out_unlock ;
965
989
966
990
off = arg -> dst_off ;
967
991
i = arg -> src_off ;
@@ -989,6 +1013,20 @@ static int io_clone_buffers(struct io_ring_ctx *ctx, struct io_ring_ctx *src_ctx
989
1013
/* Have a ref on the bufs now, drop src lock and re-grab our own lock */
990
1014
mutex_unlock (& src_ctx -> uring_lock );
991
1015
mutex_lock (& ctx -> uring_lock );
1016
+
1017
+ /*
1018
+ * If asked for replace, put the old table. data->nodes[] holds both
1019
+ * old and new nodes at this point.
1020
+ */
1021
+ if (arg -> flags & IORING_REGISTER_DST_REPLACE )
1022
+ io_rsrc_data_free (& ctx -> buf_table );
1023
+
1024
+ /*
1025
+ * ctx->buf_table should be empty now - either the contents are being
1026
+ * replaced and we just freed the table, or someone raced setting up
1027
+ * a buffer table while the clone was happening. If not empty, fall
1028
+ * through to failure handling.
1029
+ */
992
1030
if (!ctx -> buf_table .nr ) {
993
1031
ctx -> buf_table = data ;
994
1032
return 0 ;
@@ -998,14 +1036,14 @@ static int io_clone_buffers(struct io_ring_ctx *ctx, struct io_ring_ctx *src_ctx
998
1036
mutex_lock (& src_ctx -> uring_lock );
999
1037
/* someone raced setting up buffers, dump ours */
1000
1038
ret = - EBUSY ;
1001
- i = nbufs ;
1002
1039
out_put_free :
1040
+ i = data .nr ;
1003
1041
while (i -- ) {
1004
1042
io_buffer_unmap (src_ctx , data .nodes [i ]);
1005
1043
kfree (data .nodes [i ]);
1006
1044
}
1007
- io_rsrc_data_free (& data );
1008
1045
out_unlock :
1046
+ io_rsrc_data_free (& data );
1009
1047
mutex_unlock (& src_ctx -> uring_lock );
1010
1048
mutex_lock (& ctx -> uring_lock );
1011
1049
return ret ;
@@ -1025,12 +1063,12 @@ int io_register_clone_buffers(struct io_ring_ctx *ctx, void __user *arg)
1025
1063
struct file * file ;
1026
1064
int ret ;
1027
1065
1028
- if (ctx -> buf_table .nr )
1029
- return - EBUSY ;
1030
1066
if (copy_from_user (& buf , arg , sizeof (buf )))
1031
1067
return - EFAULT ;
1032
- if (buf .flags & ~IORING_REGISTER_SRC_REGISTERED )
1068
+ if (buf .flags & ~( IORING_REGISTER_SRC_REGISTERED | IORING_REGISTER_DST_REPLACE ) )
1033
1069
return - EINVAL ;
1070
+ if (!(buf .flags & IORING_REGISTER_DST_REPLACE ) && ctx -> buf_table .nr )
1071
+ return - EBUSY ;
1034
1072
if (memchr_inv (buf .pad , 0 , sizeof (buf .pad )))
1035
1073
return - EINVAL ;
1036
1074
0 commit comments