blob: 5602504a5579f3dccb5fe9e6af9d9299e0066f1f [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*/
#include <kernel/mutex.h>
#include <kernel/tee_misc.h>
#include <kernel/tee_ta_manager.h>
#include <mm/tee_mmu.h>
#include <string.h>
#include <tee_api_defines_extensions.h>
#include <tee_api_defines.h>
#include <tee/fs_dirfile.h>
#include <tee/tee_fs.h>
#include <tee/tee_obj.h>
#include <tee/tee_pobj.h>
#include <tee/tee_svc_cryp.h>
#include <tee/tee_svc.h>
#include <tee/tee_svc_storage.h>
#include <trace.h>
const struct tee_file_operations *tee_svc_storage_file_ops(uint32_t storage_id)
{
switch (storage_id) {
case TEE_STORAGE_PRIVATE:
#if defined(CFG_REE_FS)
return &ree_fs_ops;
#elif defined(CFG_RPMB_FS)
return &rpmb_fs_ops;
#else
#error At least one filesystem must be enabled.
#endif
#ifdef CFG_REE_FS
case TEE_STORAGE_PRIVATE_REE:
return &ree_fs_ops;
#endif
#ifdef CFG_RPMB_FS
case TEE_STORAGE_PRIVATE_RPMB:
return &rpmb_fs_ops;
#endif
default:
return NULL;
}
}
/* Header of GP formated secure storage files */
struct tee_svc_storage_head {
uint32_t attr_size;
uint32_t keySize;
uint32_t maxKeySize;
uint32_t objectUsage;
uint32_t objectType;
uint32_t have_attrs;
};
struct tee_storage_enum {
TAILQ_ENTRY(tee_storage_enum) link;
struct tee_fs_dir *dir;
const struct tee_file_operations *fops;
};
static TEE_Result tee_svc_storage_get_enum(struct user_ta_ctx *utc,
uint32_t enum_id,
struct tee_storage_enum **e_out)
{
struct tee_storage_enum *e;
TAILQ_FOREACH(e, &utc->storage_enums, link) {
if (enum_id == (vaddr_t)e) {
*e_out = e;
return TEE_SUCCESS;
}
}
return TEE_ERROR_BAD_PARAMETERS;
}
static TEE_Result tee_svc_close_enum(struct user_ta_ctx *utc,
struct tee_storage_enum *e)
{
if (e == NULL || utc == NULL)
return TEE_ERROR_BAD_PARAMETERS;
TAILQ_REMOVE(&utc->storage_enums, e, link);
if (e->fops)
e->fops->closedir(e->dir);
e->dir = NULL;
e->fops = NULL;
free(e);
return TEE_SUCCESS;
}
/* "/TA_uuid/object_id" or "/TA_uuid/.object_id" */
TEE_Result tee_svc_storage_create_filename(void *buf, size_t blen,
struct tee_pobj *po, bool transient)
{
uint8_t *file = buf;
uint32_t pos = 0;
uint32_t hslen = 1 /* Leading slash */
+ TEE_B2HS_HSBUF_SIZE(sizeof(TEE_UUID) + po->obj_id_len)
+ 1; /* Intermediate slash */
/* +1 for the '.' (temporary persistent object) */
if (transient)
hslen++;
if (blen < hslen)
return TEE_ERROR_SHORT_BUFFER;
file[pos++] = '/';
pos += tee_b2hs((uint8_t *)&po->uuid, &file[pos],
sizeof(TEE_UUID), hslen);
file[pos++] = '/';
if (transient)
file[pos++] = '.';
tee_b2hs(po->obj_id, file + pos, po->obj_id_len, hslen - pos);
return TEE_SUCCESS;
}
#ifdef CFG_REE_FS
/* "/dirf.db" or "/<file number>" */
TEE_Result
tee_svc_storage_create_filename_dfh(void *buf, size_t blen,
const struct tee_fs_dirfile_fileh *dfh)
{
char *file = buf;
size_t pos = 0;
size_t l;
if (pos >= blen)
return TEE_ERROR_SHORT_BUFFER;
file[pos] = '/';
pos++;
if (pos >= blen)
return TEE_ERROR_SHORT_BUFFER;
l = blen - pos;
return tee_fs_dirfile_fileh_to_fname(dfh, file + pos, &l);
}
#endif
/* "/TA_uuid" */
TEE_Result tee_svc_storage_create_dirname(void *buf, size_t blen,
const TEE_UUID *uuid)
{
uint8_t *dir = buf;
uint32_t hslen = TEE_B2HS_HSBUF_SIZE(sizeof(TEE_UUID)) + 1;
if (blen < hslen)
return TEE_ERROR_SHORT_BUFFER;
dir[0] = '/';
tee_b2hs((uint8_t *)uuid, dir + 1, sizeof(TEE_UUID), hslen);
return TEE_SUCCESS;
}
static TEE_Result tee_svc_storage_remove_corrupt_obj(
struct tee_ta_session *sess,
struct tee_obj *o)
{
o->pobj->fops->remove(o->pobj);
tee_obj_close(to_user_ta_ctx(sess->ctx), o);
return TEE_SUCCESS;
}
static TEE_Result tee_svc_storage_read_head(struct tee_obj *o)
{
TEE_Result res = TEE_SUCCESS;
size_t bytes;
struct tee_svc_storage_head head;
const struct tee_file_operations *fops = o->pobj->fops;
void *attr = NULL;
size_t size;
size_t tmp = 0;
assert(!o->fh);
res = fops->open(o->pobj, &size, &o->fh);
if (res != TEE_SUCCESS)
goto exit;
/* read head */
bytes = sizeof(struct tee_svc_storage_head);
res = fops->read(o->fh, 0, &head, &bytes);
if (res != TEE_SUCCESS) {
if (res == TEE_ERROR_CORRUPT_OBJECT)
EMSG("Head corrupt");
goto exit;
}
if (ADD_OVERFLOW(sizeof(head), head.attr_size, &tmp)) {
res = TEE_ERROR_OVERFLOW;
goto exit;
}
if (tmp > size) {
res = TEE_ERROR_CORRUPT_OBJECT;
goto exit;
}
if (bytes != sizeof(struct tee_svc_storage_head)) {
res = TEE_ERROR_BAD_FORMAT;
goto exit;
}
res = tee_obj_set_type(o, head.objectType, head.maxKeySize);
if (res != TEE_SUCCESS)
goto exit;
o->ds_pos = tmp;
if (head.attr_size) {
attr = malloc(head.attr_size);
if (!attr) {
res = TEE_ERROR_OUT_OF_MEMORY;
goto exit;
}
/* read meta */
bytes = head.attr_size;
res = fops->read(o->fh, sizeof(struct tee_svc_storage_head),
attr, &bytes);
if (res == TEE_ERROR_OUT_OF_MEMORY)
goto exit;
if (res != TEE_SUCCESS || bytes != head.attr_size)
res = TEE_ERROR_CORRUPT_OBJECT;
if (res)
goto exit;
}
res = tee_obj_attr_from_binary(o, attr, head.attr_size);
if (res != TEE_SUCCESS)
goto exit;
o->info.dataSize = size - sizeof(head) - head.attr_size;
o->info.keySize = head.keySize;
o->info.objectUsage = head.objectUsage;
o->info.objectType = head.objectType;
o->have_attrs = head.have_attrs;
exit:
free(attr);
return res;
}
TEE_Result syscall_storage_obj_open(unsigned long storage_id, void *object_id,
size_t object_id_len, unsigned long flags,
uint32_t *obj)
{
const struct tee_file_operations *fops =
tee_svc_storage_file_ops(storage_id);
struct tee_ta_session *sess = NULL;
struct user_ta_ctx *utc = NULL;
TEE_Result res = TEE_SUCCESS;
struct tee_pobj *po = NULL;
struct tee_obj *o = NULL;
char *file = NULL;
if (!fops) {
res = TEE_ERROR_ITEM_NOT_FOUND;
goto exit;
}
if (object_id_len > TEE_OBJECT_ID_MAX_LEN) {
res = TEE_ERROR_BAD_PARAMETERS;
goto exit;
}
res = tee_ta_get_current_session(&sess);
if (res != TEE_SUCCESS)
goto err;
utc = to_user_ta_ctx(sess->ctx);
res = tee_mmu_check_access_rights(&utc->uctx,
TEE_MEMORY_ACCESS_READ,
(uaddr_t) object_id,
object_id_len);
if (res != TEE_SUCCESS)
goto err;
res = tee_pobj_get((void *)&sess->ctx->uuid, object_id,
object_id_len, flags, false, fops, &po);
if (res != TEE_SUCCESS)
goto err;
o = tee_obj_alloc();
if (o == NULL) {
tee_pobj_release(po);
res = TEE_ERROR_OUT_OF_MEMORY;
goto err;
}
o->info.handleFlags =
TEE_HANDLE_FLAG_PERSISTENT | TEE_HANDLE_FLAG_INITIALIZED;
o->flags = flags;
o->pobj = po;
tee_obj_add(utc, o);
res = tee_svc_storage_read_head(o);
if (res != TEE_SUCCESS) {
if (res == TEE_ERROR_CORRUPT_OBJECT) {
EMSG("Object corrupt");
goto err;
}
goto oclose;
}
res = tee_svc_copy_kaddr_to_uref(obj, o);
if (res != TEE_SUCCESS)
goto oclose;
goto exit;
oclose:
tee_obj_close(utc, o);
o = NULL;
err:
if (res == TEE_ERROR_NO_DATA || res == TEE_ERROR_BAD_FORMAT)
res = TEE_ERROR_CORRUPT_OBJECT;
if (res == TEE_ERROR_CORRUPT_OBJECT && o)
tee_svc_storage_remove_corrupt_obj(sess, o);
exit:
free(file);
file = NULL;
return res;
}
static TEE_Result tee_svc_storage_init_file(struct tee_obj *o,
struct tee_obj *attr_o, void *data,
uint32_t len)
{
TEE_Result res = TEE_SUCCESS;
struct tee_svc_storage_head head;
const struct tee_file_operations *fops = o->pobj->fops;
void *attr = NULL;
size_t attr_size = 0;
if (attr_o) {
res = tee_obj_set_type(o, attr_o->info.objectType,
attr_o->info.maxKeySize);
if (res)
return res;
res = tee_obj_attr_copy_from(o, attr_o);
if (res)
return res;
o->have_attrs = attr_o->have_attrs;
o->info.objectUsage = attr_o->info.objectUsage;
o->info.keySize = attr_o->info.keySize;
res = tee_obj_attr_to_binary(o, NULL, &attr_size);
if (res)
return res;
if (attr_size) {
attr = malloc(attr_size);
if (!attr)
return TEE_ERROR_OUT_OF_MEMORY;
res = tee_obj_attr_to_binary(o, attr, &attr_size);
if (res != TEE_SUCCESS)
goto exit;
}
} else {
res = tee_obj_set_type(o, TEE_TYPE_DATA, 0);
if (res != TEE_SUCCESS)
goto exit;
}
o->ds_pos = sizeof(struct tee_svc_storage_head) + attr_size;
/* write head */
head.attr_size = attr_size;
head.keySize = o->info.keySize;
head.maxKeySize = o->info.maxKeySize;
head.objectUsage = o->info.objectUsage;
head.objectType = o->info.objectType;
head.have_attrs = o->have_attrs;
res = fops->create(o->pobj, !!(o->flags & TEE_DATA_FLAG_OVERWRITE),
&head, sizeof(head), attr, attr_size, data, len,
&o->fh);
if (!res)
o->info.dataSize = len;
exit:
free(attr);
return res;
}
TEE_Result syscall_storage_obj_create(unsigned long storage_id, void *object_id,
size_t object_id_len, unsigned long flags,
unsigned long attr, void *data, size_t len,
uint32_t *obj)
{
const struct tee_file_operations *fops =
tee_svc_storage_file_ops(storage_id);
struct tee_ta_session *sess = NULL;
struct user_ta_ctx *utc = NULL;
struct tee_obj *attr_o = NULL;
TEE_Result res = TEE_SUCCESS;
struct tee_pobj *po = NULL;
struct tee_obj *o = NULL;
if (!fops)
return TEE_ERROR_ITEM_NOT_FOUND;
if (object_id_len > TEE_OBJECT_ID_MAX_LEN)
return TEE_ERROR_BAD_PARAMETERS;
res = tee_ta_get_current_session(&sess);
if (res != TEE_SUCCESS)
return res;
utc = to_user_ta_ctx(sess->ctx);
res = tee_mmu_check_access_rights(&utc->uctx, TEE_MEMORY_ACCESS_READ,
(uaddr_t)object_id, object_id_len);
if (res != TEE_SUCCESS)
goto err;
res = tee_pobj_get((void *)&sess->ctx->uuid, object_id,
object_id_len, flags, true, fops, &po);
if (res != TEE_SUCCESS)
goto err;
/* check rights of the provided buffer */
if (len) {
if (data) {
uint32_t f = TEE_MEMORY_ACCESS_READ |
TEE_MEMORY_ACCESS_ANY_OWNER;
res = tee_mmu_check_access_rights(&utc->uctx, f,
(uaddr_t)data, len);
if (res != TEE_SUCCESS)
goto err;
} else {
res = TEE_ERROR_BAD_PARAMETERS;
goto err;
}
}
o = tee_obj_alloc();
if (o == NULL) {
res = TEE_ERROR_OUT_OF_MEMORY;
goto err;
}
o->info.handleFlags =
TEE_HANDLE_FLAG_PERSISTENT | TEE_HANDLE_FLAG_INITIALIZED;
o->flags = flags;
o->pobj = po;
if (attr != TEE_HANDLE_NULL) {
res = tee_obj_get(utc, tee_svc_uref_to_vaddr(attr),
&attr_o);
if (res != TEE_SUCCESS)
goto err;
}
res = tee_svc_storage_init_file(o, attr_o, data, len);
if (res != TEE_SUCCESS)
goto err;
po = NULL; /* o owns it from now on */
tee_obj_add(utc, o);
res = tee_svc_copy_kaddr_to_uref(obj, o);
if (res != TEE_SUCCESS)
goto oclose;
return TEE_SUCCESS;
oclose:
tee_obj_close(utc, o);
return res;
err:
if (res == TEE_ERROR_NO_DATA || res == TEE_ERROR_BAD_FORMAT)
res = TEE_ERROR_CORRUPT_OBJECT;
if (res == TEE_ERROR_CORRUPT_OBJECT && po)
fops->remove(po);
if (o) {
fops->close(&o->fh);
tee_obj_free(o);
}
if (po)
tee_pobj_release(po);
return res;
}
TEE_Result syscall_storage_obj_del(unsigned long obj)
{
TEE_Result res;
struct tee_ta_session *sess;
struct tee_obj *o;
struct user_ta_ctx *utc;
res = tee_ta_get_current_session(&sess);
if (res != TEE_SUCCESS)
return res;
utc = to_user_ta_ctx(sess->ctx);
res = tee_obj_get(utc, tee_svc_uref_to_vaddr(obj), &o);
if (res != TEE_SUCCESS)
return res;
if (!(o->flags & TEE_DATA_FLAG_ACCESS_WRITE_META))
return TEE_ERROR_ACCESS_CONFLICT;
if (o->pobj == NULL || o->pobj->obj_id == NULL)
return TEE_ERROR_BAD_STATE;
res = o->pobj->fops->remove(o->pobj);
tee_obj_close(utc, o);
return res;
}
TEE_Result syscall_storage_obj_rename(unsigned long obj, void *object_id,
size_t object_id_len)
{
const struct tee_file_operations *fops = NULL;
struct tee_ta_session *sess = NULL;
struct user_ta_ctx *utc = NULL;
TEE_Result res = TEE_SUCCESS;
struct tee_pobj *po = NULL;
struct tee_obj *o = NULL;
char *new_file = NULL;
char *old_file = NULL;
if (object_id_len > TEE_OBJECT_ID_MAX_LEN)
return TEE_ERROR_BAD_PARAMETERS;
res = tee_ta_get_current_session(&sess);
if (res != TEE_SUCCESS)
return res;
utc = to_user_ta_ctx(sess->ctx);
res = tee_obj_get(utc, tee_svc_uref_to_vaddr(obj), &o);
if (res != TEE_SUCCESS)
return res;
if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) {
res = TEE_ERROR_BAD_STATE;
goto exit;
}
if (!(o->flags & TEE_DATA_FLAG_ACCESS_WRITE_META)) {
res = TEE_ERROR_BAD_STATE;
goto exit;
}
if (o->pobj == NULL || o->pobj->obj_id == NULL) {
res = TEE_ERROR_BAD_STATE;
goto exit;
}
res = tee_mmu_check_access_rights(&utc->uctx, TEE_MEMORY_ACCESS_READ,
(uaddr_t)object_id, object_id_len);
if (res != TEE_SUCCESS)
goto exit;
/* reserve dest name */
fops = o->pobj->fops;
res = tee_pobj_get((void *)&sess->ctx->uuid, object_id,
object_id_len, TEE_DATA_FLAG_ACCESS_WRITE_META,
false, fops, &po);
if (res != TEE_SUCCESS)
goto exit;
/* move */
res = fops->rename(o->pobj, po, false /* no overwrite */);
if (res)
goto exit;
res = tee_pobj_rename(o->pobj, object_id, object_id_len);
exit:
tee_pobj_release(po);
free(new_file);
free(old_file);
return res;
}
TEE_Result syscall_storage_alloc_enum(uint32_t *obj_enum)
{
struct tee_storage_enum *e;
struct tee_ta_session *sess;
TEE_Result res;
struct user_ta_ctx *utc;
if (obj_enum == NULL)
return TEE_ERROR_BAD_PARAMETERS;
res = tee_ta_get_current_session(&sess);
if (res != TEE_SUCCESS)
return res;
utc = to_user_ta_ctx(sess->ctx);
e = malloc(sizeof(struct tee_storage_enum));
if (e == NULL)
return TEE_ERROR_OUT_OF_MEMORY;
e->dir = NULL;
e->fops = NULL;
TAILQ_INSERT_TAIL(&utc->storage_enums, e, link);
return tee_svc_copy_kaddr_to_uref(obj_enum, e);
}
TEE_Result syscall_storage_free_enum(unsigned long obj_enum)
{
struct tee_storage_enum *e;
TEE_Result res;
struct tee_ta_session *sess;
struct user_ta_ctx *utc;
res = tee_ta_get_current_session(&sess);
if (res != TEE_SUCCESS)
return res;
utc = to_user_ta_ctx(sess->ctx);
res = tee_svc_storage_get_enum(utc,
tee_svc_uref_to_vaddr(obj_enum), &e);
if (res != TEE_SUCCESS)
return res;
return tee_svc_close_enum(utc, e);
}
TEE_Result syscall_storage_reset_enum(unsigned long obj_enum)
{
struct tee_storage_enum *e;
TEE_Result res;
struct tee_ta_session *sess;
res = tee_ta_get_current_session(&sess);
if (res != TEE_SUCCESS)
return res;
res = tee_svc_storage_get_enum(to_user_ta_ctx(sess->ctx),
tee_svc_uref_to_vaddr(obj_enum), &e);
if (res != TEE_SUCCESS)
return res;
if (e->fops) {
e->fops->closedir(e->dir);
e->fops = NULL;
e->dir = NULL;
}
assert(!e->dir);
return TEE_SUCCESS;
}
static TEE_Result tee_svc_storage_set_enum(struct tee_fs_dirent *d,
const struct tee_file_operations *fops,
struct tee_obj *o)
{
o->info.handleFlags =
TEE_HANDLE_FLAG_PERSISTENT | TEE_HANDLE_FLAG_INITIALIZED;
o->info.objectUsage = TEE_USAGE_DEFAULT;
if (d->oidlen > TEE_OBJECT_ID_MAX_LEN)
return TEE_ERROR_CORRUPT_OBJECT;
o->pobj->obj_id = malloc(d->oidlen);
if (!o->pobj->obj_id)
return TEE_ERROR_OUT_OF_MEMORY;
memcpy(o->pobj->obj_id, d->oid, d->oidlen);
o->pobj->obj_id_len = d->oidlen;
o->pobj->fops = fops;
return TEE_SUCCESS;
}
TEE_Result syscall_storage_start_enum(unsigned long obj_enum,
unsigned long storage_id)
{
struct tee_storage_enum *e;
TEE_Result res;
struct tee_ta_session *sess;
const struct tee_file_operations *fops =
tee_svc_storage_file_ops(storage_id);
res = tee_ta_get_current_session(&sess);
if (res != TEE_SUCCESS)
return res;
res = tee_svc_storage_get_enum(to_user_ta_ctx(sess->ctx),
tee_svc_uref_to_vaddr(obj_enum), &e);
if (res != TEE_SUCCESS)
return res;
if (e->dir) {
e->fops->closedir(e->dir);
e->dir = NULL;
}
if (!fops)
return TEE_ERROR_ITEM_NOT_FOUND;
e->fops = fops;
return fops->opendir(&sess->ctx->uuid, &e->dir);
}
TEE_Result syscall_storage_next_enum(unsigned long obj_enum,
TEE_ObjectInfo *info, void *obj_id, uint64_t *len)
{
struct tee_ta_session *sess = NULL;
struct tee_storage_enum *e = NULL;
struct tee_fs_dirent *d = NULL;
struct user_ta_ctx *utc = NULL;
TEE_Result res = TEE_SUCCESS;
struct tee_obj *o = NULL;
uint64_t l = 0;
res = tee_ta_get_current_session(&sess);
if (res != TEE_SUCCESS)
goto exit;
utc = to_user_ta_ctx(sess->ctx);
res = tee_svc_storage_get_enum(utc,
tee_svc_uref_to_vaddr(obj_enum), &e);
if (res != TEE_SUCCESS)
goto exit;
/* check rights of the provided buffers */
res = tee_mmu_check_access_rights(&utc->uctx,
TEE_MEMORY_ACCESS_WRITE |
TEE_MEMORY_ACCESS_ANY_OWNER,
(uaddr_t)info,
sizeof(TEE_ObjectInfo));
if (res != TEE_SUCCESS)
goto exit;
res = tee_mmu_check_access_rights(&utc->uctx,
TEE_MEMORY_ACCESS_WRITE |
TEE_MEMORY_ACCESS_ANY_OWNER,
(uaddr_t)obj_id,
TEE_OBJECT_ID_MAX_LEN);
if (res != TEE_SUCCESS)
goto exit;
if (!e->fops) {
res = TEE_ERROR_ITEM_NOT_FOUND;
goto exit;
}
res = e->fops->readdir(e->dir, &d);
if (res != TEE_SUCCESS)
goto exit;
o = tee_obj_alloc();
if (o == NULL) {
res = TEE_ERROR_OUT_OF_MEMORY;
goto exit;
}
o->flags = TEE_DATA_FLAG_SHARE_READ;
o->pobj = calloc(1, sizeof(struct tee_pobj));
if (!o->pobj) {
res = TEE_ERROR_OUT_OF_MEMORY;
goto exit;
}
o->pobj->uuid = sess->ctx->uuid;
res = tee_svc_storage_set_enum(d, e->fops, o);
if (res != TEE_SUCCESS)
goto exit;
res = tee_svc_storage_read_head(o);
if (res != TEE_SUCCESS)
goto exit;
memcpy(info, &o->info, sizeof(TEE_ObjectInfo));
memcpy(obj_id, o->pobj->obj_id, o->pobj->obj_id_len);
l = o->pobj->obj_id_len;
res = tee_svc_copy_to_user(len, &l, sizeof(*len));
exit:
if (o) {
if (o->pobj) {
if (o->pobj->fops)
o->pobj->fops->close(&o->fh);
free(o->pobj->obj_id);
}
free(o->pobj);
tee_obj_free(o);
}
return res;
}
TEE_Result syscall_storage_obj_read(unsigned long obj, void *data, size_t len,
uint64_t *count)
{
struct tee_ta_session *sess = NULL;
struct user_ta_ctx *utc = NULL;
TEE_Result res = TEE_SUCCESS;
struct tee_obj *o = NULL;
uint64_t u_count = 0;
size_t pos_tmp = 0;
size_t bytes = 0;
res = tee_ta_get_current_session(&sess);
if (res != TEE_SUCCESS)
goto exit;
utc = to_user_ta_ctx(sess->ctx);
res = tee_obj_get(utc, tee_svc_uref_to_vaddr(obj), &o);
if (res != TEE_SUCCESS)
goto exit;
if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) {
res = TEE_ERROR_BAD_STATE;
goto exit;
}
if (!(o->flags & TEE_DATA_FLAG_ACCESS_READ)) {
res = TEE_ERROR_ACCESS_CONFLICT;
goto exit;
}
/* Guard o->info.dataPosition += bytes below from overflowing */
if (ADD_OVERFLOW(o->info.dataPosition, len, &pos_tmp)) {
res = TEE_ERROR_OVERFLOW;
goto exit;
}
/* check rights of the provided buffer */
res = tee_mmu_check_access_rights(&utc->uctx,
TEE_MEMORY_ACCESS_WRITE |
TEE_MEMORY_ACCESS_ANY_OWNER,
(uaddr_t)data, len);
if (res != TEE_SUCCESS)
goto exit;
bytes = len;
if (ADD_OVERFLOW(o->ds_pos, o->info.dataPosition, &pos_tmp)) {
res = TEE_ERROR_OVERFLOW;
goto exit;
}
res = o->pobj->fops->read(o->fh, pos_tmp, data, &bytes);
if (res != TEE_SUCCESS) {
if (res == TEE_ERROR_CORRUPT_OBJECT) {
EMSG("Object corrupt");
tee_svc_storage_remove_corrupt_obj(sess, o);
}
goto exit;
}
o->info.dataPosition += bytes;
u_count = bytes;
res = tee_svc_copy_to_user(count, &u_count, sizeof(*count));
exit:
return res;
}
TEE_Result syscall_storage_obj_write(unsigned long obj, void *data, size_t len)
{
struct tee_ta_session *sess = NULL;
struct user_ta_ctx *utc = NULL;
TEE_Result res = TEE_SUCCESS;
struct tee_obj *o = NULL;
size_t pos_tmp = 0;
res = tee_ta_get_current_session(&sess);
if (res != TEE_SUCCESS)
goto exit;
utc = to_user_ta_ctx(sess->ctx);
res = tee_obj_get(utc, tee_svc_uref_to_vaddr(obj), &o);
if (res != TEE_SUCCESS)
goto exit;
if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) {
res = TEE_ERROR_BAD_STATE;
goto exit;
}
if (!(o->flags & TEE_DATA_FLAG_ACCESS_WRITE)) {
res = TEE_ERROR_ACCESS_CONFLICT;
goto exit;
}
/* Guard o->info.dataPosition += bytes below from overflowing */
if (ADD_OVERFLOW(o->info.dataPosition, len, &pos_tmp)) {
res = TEE_ERROR_OVERFLOW;
goto exit;
}
/* check rights of the provided buffer */
res = tee_mmu_check_access_rights(&utc->uctx,
TEE_MEMORY_ACCESS_READ |
TEE_MEMORY_ACCESS_ANY_OWNER,
(uaddr_t)data, len);
if (res != TEE_SUCCESS)
goto exit;
if (ADD_OVERFLOW(o->ds_pos, o->info.dataPosition, &pos_tmp)) {
res = TEE_ERROR_ACCESS_CONFLICT;
goto exit;
}
res = o->pobj->fops->write(o->fh, pos_tmp, data, len);
if (res != TEE_SUCCESS)
goto exit;
o->info.dataPosition += len;
if (o->info.dataPosition > o->info.dataSize)
o->info.dataSize = o->info.dataPosition;
exit:
return res;
}
TEE_Result syscall_storage_obj_trunc(unsigned long obj, size_t len)
{
TEE_Result res;
struct tee_ta_session *sess;
struct tee_obj *o;
size_t off;
size_t attr_size;
res = tee_ta_get_current_session(&sess);
if (res != TEE_SUCCESS)
goto exit;
res = tee_obj_get(to_user_ta_ctx(sess->ctx),
tee_svc_uref_to_vaddr(obj), &o);
if (res != TEE_SUCCESS)
goto exit;
if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) {
res = TEE_ERROR_BAD_STATE;
goto exit;
}
if (!(o->flags & TEE_DATA_FLAG_ACCESS_WRITE)) {
res = TEE_ERROR_ACCESS_CONFLICT;
goto exit;
}
res = tee_obj_attr_to_binary(o, NULL, &attr_size);
if (res != TEE_SUCCESS)
goto exit;
if (ADD_OVERFLOW(sizeof(struct tee_svc_storage_head), attr_size,
&off)) {
res = TEE_ERROR_OVERFLOW;
goto exit;
}
if (ADD_OVERFLOW(len, off, &off)) {
res = TEE_ERROR_OVERFLOW;
goto exit;
}
res = o->pobj->fops->truncate(o->fh, off);
switch (res) {
case TEE_SUCCESS:
o->info.dataSize = len;
break;
case TEE_ERROR_CORRUPT_OBJECT:
EMSG("Object corruption");
(void)tee_svc_storage_remove_corrupt_obj(sess, o);
break;
default:
res = TEE_ERROR_GENERIC;
break;
}
exit:
return res;
}
TEE_Result syscall_storage_obj_seek(unsigned long obj, int32_t offset,
unsigned long whence)
{
TEE_Result res;
struct tee_ta_session *sess;
struct tee_obj *o;
tee_fs_off_t new_pos;
res = tee_ta_get_current_session(&sess);
if (res != TEE_SUCCESS)
return res;
res = tee_obj_get(to_user_ta_ctx(sess->ctx),
tee_svc_uref_to_vaddr(obj), &o);
if (res != TEE_SUCCESS)
return res;
if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT))
return TEE_ERROR_BAD_STATE;
switch (whence) {
case TEE_DATA_SEEK_SET:
new_pos = offset;
break;
case TEE_DATA_SEEK_CUR:
if (ADD_OVERFLOW(o->info.dataPosition, offset, &new_pos))
return TEE_ERROR_OVERFLOW;
break;
case TEE_DATA_SEEK_END:
if (ADD_OVERFLOW(o->info.dataSize, offset, &new_pos))
return TEE_ERROR_OVERFLOW;
break;
default:
return TEE_ERROR_BAD_PARAMETERS;
}
if (new_pos < 0)
new_pos = 0;
if (new_pos > TEE_DATA_MAX_POSITION) {
EMSG("Position is beyond TEE_DATA_MAX_POSITION");
return TEE_ERROR_BAD_PARAMETERS;
}
o->info.dataPosition = new_pos;
return TEE_SUCCESS;
}
void tee_svc_storage_close_all_enum(struct user_ta_ctx *utc)
{
struct tee_storage_enum_head *eh = &utc->storage_enums;
/* disregard return value */
while (!TAILQ_EMPTY(eh))
tee_svc_close_enum(utc, TAILQ_FIRST(eh));
}