Am Donnerstag, 16. März 2017, 10:52:48 CEST schrieb Herbert Xu:
Hi Herbert,
> More importantly, with the current code, a very large recvmsg
> would still work by doing it piecemeal. With your patch, won't
> it fail because sock_kmalloc could fail to allocate memory for
> the whole thing?
For testing purpose, I wrote the app that is attached. It simply encrypts a
given file with ctr(aes).
On the current implementation of algif_skcipher where I directly pipe in the
provided memory block (i.e. using the stream operation of libkcapi):
dd if=/dev/zero of=testfile bs=1024 count=1000000
./kcapi-long testfile testfile.out
encryption failed with error -14
==> The -EFAULT happens at sendmsg() -- i.e. you cannot provide as much data
as you want.
==> On the proposed update, I see the same error.
When I use vmsplice, the current implementation somehow simply waits (I do not
yet know what happens). My new implementation simply returns the amount of
data that could have been spliced (64k -- which would allow user space to
implement a loop to send chunks).
When I ask libkcapi to send the data in chunks, libkcapi implements the loop
that sends/receives the data. In this case, both implementations of
algif_skcipher.c work to encrypt the whole file regardless of its size.
Thus, I would conclude that the current outer loop in recvmsg of the current
algif_skcipher is not really helpful as the bottleneck is on the sendmsg side.
With this, I would conclude that the new implementation of algif_skcipher.c
proposed in this patch set has the same behavior as the old one.
Ciao
Stephan
/*
* Copyright (C) 2017, Stephan Mueller <smueller@xxxxxxxxxx>
*
* License: see LICENSE file in root directory
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
* WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <kcapi.h>
static int check_filetype(int fd, struct stat *sb, const char *filename)
{
fstat(fd, sb);
/* Do not return an error in case we cannot validate the data. */
if ((sb->st_mode & S_IFMT) != S_IFREG &&
(sb->st_mode & S_IFMT) != S_IFLNK) {
fprintf(stderr, "%s is no regular file or symlink\n", filename);
return -EINVAL;
}
return 0;
}
static int crypt(struct kcapi_handle *handle, const uint8_t *iv,
const char *infile, const char *outfile)
{
int infd = -1, outfd = -1;
int ret = 0;
struct stat insb, outsb;
uint8_t *inmem = NULL, *outmem = NULL;
size_t outsize;
infd = open(infile, O_RDONLY | O_CLOEXEC);
if (infd < 0) {
fprintf(stderr, "Cannot open file %s: %s\n", infile,
strerror(errno));
return -EIO;
}
outfd = open(outfile, O_RDWR | O_CLOEXEC | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
if (outfd < 0) {
fprintf(stderr, "Cannot open file %s: %s\n", infile,
strerror(errno));
ret = -EIO;
goto out;
}
ret = check_filetype(infd, &insb, infile);
if (ret)
goto out;
ret = check_filetype(outfd, &outsb, outfile);
if (ret)
goto out;
if (insb.st_size) {
inmem = mmap(NULL, insb.st_size, PROT_READ, MAP_SHARED,
infd, 0);
if (inmem == MAP_FAILED)
{
fprintf(stderr, "Use of mmap failed\n");
ret = -ENOMEM;
goto out;
}
}
outsize = ((insb.st_size + kcapi_cipher_blocksize(handle) - 1) /
kcapi_cipher_blocksize(handle)) *
kcapi_cipher_blocksize(handle);
ret = ftruncate(outfd, outsize);
if (ret)
goto out;
if (outsize) {
outmem = mmap(NULL, outsize, PROT_WRITE, MAP_SHARED,
outfd, 0);
if (outmem == MAP_FAILED)
{
fprintf(stderr, "Use of mmap failed\n");
ret = -ENOMEM;
goto out;
}
}
#if 1
/* Send all data in one go, libkcapi will not loop */
struct iovec iniov, outiov;
iniov.iov_base = inmem;
iniov.iov_len = insb.st_size;
outiov.iov_base = outmem;
outiov.iov_len = outsize;
ret = kcapi_cipher_stream_init_enc(handle, iv, NULL, 0);
if (ret)
goto out;
ret = kcapi_cipher_stream_update(handle, &iniov, 1);
if (ret)
goto out;
ret = kcapi_cipher_stream_op(handle, &outiov, 1);
#else
/* libkcapi will loop over the data and send it in chunks */
ret = kcapi_cipher_encrypt(handle, inmem, insb.st_size, iv,
outmem, outsize, KCAPI_ACCESS_SENDMSG);
#endif
out:
if (inmem && inmem != MAP_FAILED)
munmap(inmem, insb.st_size);
if (outmem && outmem != MAP_FAILED)
munmap(outmem, outsize);
if (infd >= 0)
close(infd);
if (outfd >= 0)
close(outfd);
return ret;
}
int main(int argc, char *argv[])
{
struct kcapi_handle *handle = NULL;
int ret;
if (argc != 3) {
fprintf(stderr, "infile, outfile required\n");
return -EINVAL;
}
/* with CTR mode, we can skip any padding */
ret = kcapi_cipher_init(&handle, "ctr(aes)", 0);
if (ret)
return ret;
ret = kcapi_cipher_setkey(handle, (uint8_t *)"0123456789012345", 16);
if (ret)
goto out;
ret = crypt(handle, (uint8_t *)"0123456789012345", argv[1], argv[2]);
if (ret > 0) {
fprintf(stderr, "%d bytes of ciphertext created\n", ret);
ret = 0;
} else {
fprintf(stderr, "encryption failed with error %d\n", ret);
}
out:
kcapi_cipher_destroy(handle);
return ret;
}