Track resources and enforce cpu/memory/interface limits for non-root users.
authorreyk <reyk@openbsd.org>
Sun, 15 Jul 2018 14:36:54 +0000 (14:36 +0000)
committerreyk <reyk@openbsd.org>
Sun, 15 Jul 2018 14:36:54 +0000 (14:36 +0000)
The limits are currently hard-coded and undocumented (4 CPUs/VMs, 2G
memory, 8 interfaces) but will be configurable in an upcoming diff.
These limits are tracked in total usage; for example, a user will be
able to run up to 4 VMs with 512M of memory or a single VM with 2G.

OK ccardenas@ mlarkin@

usr.sbin/vmd/config.c
usr.sbin/vmd/proc.h
usr.sbin/vmd/vioscsi.c
usr.sbin/vmd/vmd.c
usr.sbin/vmd/vmd.h

index 5f6db1a..ac5f180 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: config.c,v 1.47 2018/07/13 10:26:57 reyk Exp $        */
+/*     $OpenBSD: config.c,v 1.48 2018/07/15 14:36:54 reyk Exp $        */
 
 /*
  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -68,6 +68,12 @@ config_init(struct vmd *env)
                        return (-1);
                TAILQ_INIT(env->vmd_switches);
        }
+       if (what & CONFIG_USERS) {
+               if ((env->vmd_users = calloc(1,
+                   sizeof(*env->vmd_users))) == NULL)
+                       return (-1);
+               TAILQ_INIT(env->vmd_users);
+       }
 
        return (0);
 }
@@ -187,7 +193,16 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
        if (vm->vm_running) {
                log_warnx("%s: vm is already running", __func__);
                errno = EALREADY;
-               goto fail;
+               return (-1);
+       }
+
+       /* increase the user reference counter and check user limits */
+       if (vm->vm_user != NULL && user_get(vm->vm_user->usr_id.uid) != NULL) {
+               user_inc(vcp, vm->vm_user, 1);
+               if (user_checklimit(vm->vm_user, vcp) == -1) {
+                       errno = EPERM;
+                       goto fail;
+               }
        }
 
        diskfds = reallocarray(NULL, vcp->vcp_ndisks, sizeof(*diskfds));
index 323b57a..f0e4704 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: proc.h,v 1.13 2018/06/26 10:00:08 reyk Exp $  */
+/*     $OpenBSD: proc.h,v 1.14 2018/07/15 14:36:54 reyk Exp $  */
 
 /*
  * Copyright (c) 2010-2015 Reyk Floeter <reyk@openbsd.org>
@@ -98,6 +98,7 @@ enum privsep_procid {
 #define CONFIG_RELOAD          0x00
 #define CONFIG_VMS             0x01
 #define CONFIG_SWITCHES                0x02
+#define CONFIG_USERS           0x04
 #define CONFIG_ALL             0xff
 
 struct privsep_pipes {
index f8b44a7..091136d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vioscsi.c,v 1.7 2018/07/10 20:43:15 reyk Exp $  */
+/*     $OpenBSD: vioscsi.c,v 1.8 2018/07/15 14:36:54 reyk Exp $  */
 
 /*
  * Copyright (c) 2017 Carlos Cardenas <ccardenas@openbsd.org>
@@ -261,7 +261,7 @@ vioscsi_handle_inquiry(struct vioscsi_dev *dev,
        inq = (struct scsi_inquiry *)(req->cdb);
        inq_len = (uint16_t)_2btol(inq->length);
 
-       log_debug("%s: INQ - EVPD %d PAGE_CODE 0x%08x LEN %d", __func__,
+       DPRINTF("%s: INQ - EVPD %d PAGE_CODE 0x%08x LEN %d", __func__,
            inq->flags & SI_EVPD, inq->pagecode, inq_len);
 
        vioscsi_prepare_resp(&resp,
@@ -341,7 +341,7 @@ vioscsi_handle_mode_sense(struct vioscsi_dev *dev,
        mode_page_ctl = mode_sense->page & SMS_PAGE_CTRL;
        mode_page_code = mode_sense->page & SMS_PAGE_CODE;
 
-       log_debug("%s: M_SENSE - DBD %d Page Ctrl 0x%x Code 0x%x Len %u",
+       DPRINTF("%s: M_SENSE - DBD %d Page Ctrl 0x%x Code 0x%x Len %u",
            __func__, mode_sense->byte2 & SMS_DBD, mode_page_ctl,
            mode_page_code, mode_sense->length);
 
@@ -484,7 +484,7 @@ vioscsi_handle_mode_sense_big(struct vioscsi_dev *dev,
        mode_page_code = mode_sense_10->page & SMS_PAGE_CODE;
        mode_sense_len = (uint16_t)_2btol(mode_sense_10->length);
 
-       log_debug("%s: M_SENSE_10 - DBD %d Page Ctrl 0x%x Code 0x%x Len %u",
+       DPRINTF("%s: M_SENSE_10 - DBD %d Page Ctrl 0x%x Code 0x%x Len %u",
            __func__, mode_sense_10->byte2 & SMS_DBD, mode_page_ctl,
            mode_page_code, mode_sense_len);
 
@@ -620,7 +620,7 @@ vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
        memset(&resp, 0, sizeof(resp));
        r_cap = (struct scsi_read_capacity *)(req->cdb);
        r_cap_addr = _4btol(r_cap->addr);
-       log_debug("%s: %s - Addr 0x%08x", __func__,
+       DPRINTF("%s: %s - Addr 0x%08x", __func__,
            vioscsi_op_names(r_cap->opcode), r_cap_addr);
 
        vioscsi_prepare_resp(&resp,
@@ -633,7 +633,7 @@ vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
                goto read_capacity_out;
        }
 
-       log_debug("%s: ISO has %lld bytes and %lld blocks",
+       DPRINTF("%s: ISO has %lld bytes and %lld blocks",
            __func__, dev->sz, dev->n_blocks);
 
        /*
@@ -709,7 +709,7 @@ vioscsi_handle_read_capacity_16(struct vioscsi_dev *dev,
        memset(&resp, 0, sizeof(resp));
        r_cap_16 = (struct scsi_read_capacity_16 *)(req->cdb);
        r_cap_addr_16 = _8btol(r_cap_16->addr);
-       log_debug("%s: %s - Addr 0x%016llx", __func__,
+       DPRINTF("%s: %s - Addr 0x%016llx", __func__,
            vioscsi_op_names(r_cap_16->opcode), r_cap_addr_16);
 
        vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
@@ -722,7 +722,7 @@ vioscsi_handle_read_capacity_16(struct vioscsi_dev *dev,
                goto read_capacity_16_out;
        }
 
-       log_debug("%s: ISO has %lld bytes and %lld blocks", __func__,
+       DPRINTF("%s: ISO has %lld bytes and %lld blocks", __func__,
            dev->sz, dev->n_blocks);
 
        _lto8b(dev->n_blocks - 1, r_cap_data_16->addr);
@@ -785,11 +785,11 @@ vioscsi_handle_report_luns(struct vioscsi_dev *dev,
        rpl = (struct scsi_report_luns *)(req->cdb);
        rpl_length = _4btol(rpl->length);
 
-       log_debug("%s: REPORT_LUNS Report 0x%x Length %d", __func__,
+       DPRINTF("%s: REPORT_LUNS Report 0x%x Length %d", __func__,
            rpl->selectreport, rpl_length);
 
        if (rpl_length < RPL_MIN_SIZE) {
-               log_debug("%s: RPL_Length %d < %d (RPL_MIN_SIZE)", __func__,
+               DPRINTF("%s: RPL_Length %d < %d (RPL_MIN_SIZE)", __func__,
                    rpl_length, RPL_MIN_SIZE);
 
                vioscsi_prepare_resp(&resp,
@@ -886,12 +886,12 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
        read_lba = ((read_6->addr[0] & SRW_TOPADDR) << 16 ) |
            (read_6->addr[1] << 8) | read_6->addr[2];
 
-       log_debug("%s: READ Addr 0x%08x Len %d (%d)",
+       DPRINTF("%s: READ Addr 0x%08x Len %d (%d)",
            __func__, read_lba, read_6->length, read_6->length * dev->max_xfer);
 
        /* check if lba is in range */
        if (read_lba > dev->n_blocks - 1) {
-               log_debug("%s: requested block out of range req: %ud max: %lld",
+               DPRINTF("%s: requested block out of range req: %ud max: %lld",
                    __func__, read_lba, dev->n_blocks);
 
                vioscsi_prepare_resp(&resp,
@@ -1017,12 +1017,12 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
        read_10_len = _2btol(read_10->length);
        chunk_offset = 0;
 
-       log_debug("%s: READ_10 Addr 0x%08x Len %d (%d)",
+       DPRINTF("%s: READ_10 Addr 0x%08x Len %d (%d)",
            __func__, read_lba, read_10_len, read_10_len * dev->max_xfer);
 
        /* check if lba is in range */
        if (read_lba > dev->n_blocks - 1) {
-               log_debug("%s: requested block out of range req: %ud max: %lld",
+               DPRINTF("%s: requested block out of range req: %ud max: %lld",
                    __func__, read_lba, dev->n_blocks);
 
                vioscsi_prepare_resp(&resp,
@@ -1153,9 +1153,9 @@ vioscsi_handle_prevent_allow(struct vioscsi_dev *dev,
        vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
 
        if (dev->locked) {
-               log_debug("%s: unlocking medium", __func__);
+               DPRINTF("%s: unlocking medium", __func__);
        } else {
-               log_debug("%s: locking medium", __func__);
+               DPRINTF("%s: locking medium", __func__);
        }
 
        dev->locked = dev->locked ? 0 : 1;
@@ -1187,7 +1187,7 @@ vioscsi_handle_mechanism_status(struct vioscsi_dev *dev,
        memset(&resp, 0, sizeof(resp));
        mech_status = (struct scsi_mechanism_status *)(req->cdb);
        mech_status_len = (uint16_t)_2btol(mech_status->length);
-       log_debug("%s: MECH_STATUS Len %u", __func__, mech_status_len);
+       DPRINTF("%s: MECH_STATUS Len %u", __func__, mech_status_len);
 
        mech_status_header = calloc(1, sizeof(*mech_status_header));
 
@@ -1247,7 +1247,7 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
        memset(&resp, 0, sizeof(resp));
        toc = (struct scsi_read_toc *)(req->cdb);
        toc_len = (uint16_t)_2btol(toc->data_len);
-       log_debug("%s: %s - MSF %d Track 0x%02x Addr 0x%04x",
+       DPRINTF("%s: %s - MSF %d Track 0x%02x Addr 0x%04x",
            __func__, vioscsi_op_names(toc->opcode),
            ((toc->byte2 >> 1) & 1), toc->from_track, toc_len);
 
@@ -1257,7 +1257,7 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
        if (toc->from_track > 1 &&
            toc->from_track != READ_TOC_LEAD_OUT_TRACK) {
                /* illegal request */
-               log_debug("%s: illegal request Track 0x%02x",
+               DPRINTF("%s: illegal request Track 0x%02x",
                    __func__, toc->from_track);
 
                vioscsi_prepare_resp(&resp,
@@ -1385,7 +1385,7 @@ vioscsi_handle_read_disc_info(struct vioscsi_dev *dev,
        memset(&resp, 0, sizeof(resp));
        read_disc =
            (struct scsi_read_disc_information *)(req->cdb);
-       log_debug("%s: Disc Info %x", __func__, read_disc->byte2);
+       DPRINTF("%s: Disc Info %x", __func__, read_disc->byte2);
 
        /* send back unsupported */
        vioscsi_prepare_resp(&resp,
@@ -1424,7 +1424,7 @@ vioscsi_handle_gesn(struct vioscsi_dev *dev,
 
        memset(&resp, 0, sizeof(resp));
        gesn = (struct scsi_gesn *)(req->cdb);
-       log_debug("%s: GESN Method %s", __func__,
+       DPRINTF("%s: GESN Method %s", __func__,
            gesn->byte2 ? "Polling" : "Asynchronous");
 
        if (gesn->byte2 == 0) {
@@ -1530,7 +1530,7 @@ vioscsi_handle_get_config(struct vioscsi_dev *dev,
        get_configuration = (struct scsi_get_configuration *)(req->cdb);
        get_conf_feature = (uint16_t)_2btol(get_configuration->feature);
        get_conf_len = (uint16_t)_2btol(get_configuration->length);
-       log_debug("%s: Conf RT %x Feature %d Len %d", __func__,
+       DPRINTF("%s: Conf RT %x Feature %d Len %d", __func__,
            get_configuration->byte2, get_conf_feature, get_conf_len);
 
        get_conf_reply = (uint8_t*)calloc(G_CONFIG_REPLY_SIZE, sizeof(uint8_t));
@@ -1664,7 +1664,7 @@ vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
 
        *intr = 0xFF;
 
-       log_debug("%s: request %s reg %u,%s sz %u", __func__,
+       DPRINTF("%s: request %s reg %u,%s sz %u", __func__,
            dir ? "READ" : "WRITE", reg, vioscsi_reg_name(reg), sz);
 
        if (dir == 0) {
@@ -1677,7 +1677,7 @@ vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
                        break;
                case VIRTIO_CONFIG_GUEST_FEATURES:
                        dev->cfg.guest_feature = *data;
-                       log_debug("%s: guest feature set to %u",
+                       DPRINTF("%s: guest feature set to %u",
                            __func__, dev->cfg.guest_feature);
                        break;
                case VIRTIO_CONFIG_QUEUE_ADDRESS:
@@ -1695,10 +1695,10 @@ vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
                        break;
                case VIRTIO_CONFIG_DEVICE_STATUS:
                        dev->cfg.device_status = *data;
-                       log_debug("%s: device status set to %u",
+                       DPRINTF("%s: device status set to %u",
                            __func__, dev->cfg.device_status);
                        if (dev->cfg.device_status == 0) {
-                               log_debug("%s: device reset", __func__);
+                               DPRINTF("%s: device reset", __func__);
                                dev->cfg.guest_feature = 0;
                                dev->cfg.queue_address = 0;
                                vioscsi_update_qa(dev);
@@ -2156,7 +2156,7 @@ vioscsi_notifyq(struct vioscsi_dev *dev)
                 * respond with a BAD_TARGET response.
                 */
                if (req.lun[1] >= VIOSCSI_MAX_TARGET || req.lun[3] > 0) {
-                       log_debug("%s: Ignore CMD 0x%02x,%s on lun %u:%u:%u:%u",
+                       DPRINTF("%s: Ignore CMD 0x%02x,%s on lun %u:%u:%u:%u",
                            __func__, req.cdb[0], vioscsi_op_names(req.cdb[0]),
                            req.lun[0], req.lun[1], req.lun[2], req.lun[3]);
                        /* Move index for response */
@@ -2187,7 +2187,7 @@ vioscsi_notifyq(struct vioscsi_dev *dev)
                        goto next_msg;
                }
 
-               log_debug("%s: Queue %d id 0x%llx lun %u:%u:%u:%u"
+               DPRINTF("%s: Queue %d id 0x%llx lun %u:%u:%u:%u"
                    " cdb OP 0x%02x,%s",
                    __func__, dev->cfg.queue_notify, req.id,
                    req.lun[0], req.lun[1], req.lun[2], req.lun[3],
index da18462..2b07a84 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmd.c,v 1.97 2018/07/13 10:26:57 reyk Exp $   */
+/*     $OpenBSD: vmd.c,v 1.98 2018/07/15 14:36:54 reyk Exp $   */
 
 /*
  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -36,6 +36,7 @@
 #include <signal.h>
 #include <syslog.h>
 #include <unistd.h>
+#include <util.h>
 #include <ctype.h>
 #include <pwd.h>
 #include <grp.h>
@@ -1090,6 +1091,9 @@ vm_stop(struct vmd_vm *vm, int keeptty, const char *caller)
        vm->vm_running = 0;
        vm->vm_shutdown = 0;
 
+       user_inc(&vm->vm_params.vmc_params, vm->vm_user, 0);
+       user_put(vm->vm_user);
+
        if (vm->vm_iev.ibuf.fd != -1) {
                event_del(&vm->vm_iev.ev);
                close(vm->vm_iev.ibuf.fd);
@@ -1140,6 +1144,7 @@ vm_remove(struct vmd_vm *vm, const char *caller)
 
        TAILQ_REMOVE(env->vmd_vms, vm, vm_entry);
 
+       user_put(vm->vm_user);
        vm_stop(vm, 0, caller);
        free(vm);
 }
@@ -1151,6 +1156,7 @@ vm_register(struct privsep *ps, struct vmop_create_params *vmc,
        struct vmd_vm           *vm = NULL, *vm_parent = NULL;
        struct vm_create_params *vcp = &vmc->vmc_params;
        struct vmop_owner       *vmo = NULL;
+       struct vmd_user         *usr = NULL;
        uint32_t                 rng;
        unsigned int             i;
        struct vmd_switch       *sw;
@@ -1223,6 +1229,13 @@ vm_register(struct privsep *ps, struct vmop_create_params *vmc,
                }
        }
 
+       /* track active users */
+       if (uid != 0 && env->vmd_users != NULL &&
+           (usr = user_get(uid)) == NULL) {
+               log_warnx("could not add user");
+               goto fail;
+       }
+
        if ((vm = calloc(1, sizeof(*vm))) == NULL)
                goto fail;
 
@@ -1233,6 +1246,7 @@ vm_register(struct privsep *ps, struct vmop_create_params *vmc,
        vm->vm_tty = -1;
        vm->vm_receive_fd = -1;
        vm->vm_paused = 0;
+       vm->vm_user = usr;
 
        for (i = 0; i < vcp->vcp_ndisks; i++)
                vm->vm_disks[i] = -1;
@@ -1765,6 +1779,104 @@ switch_getbyname(const char *name)
        return (NULL);
 }
 
+struct vmd_user *
+user_get(uid_t uid)
+{
+       struct vmd_user         *usr;
+
+       if (uid == 0)
+               return (NULL);
+
+       /* first try to find an existing user */
+       TAILQ_FOREACH(usr, env->vmd_users, usr_entry) {
+               if (usr->usr_id.uid == uid)
+                       goto done;
+       }
+
+       if ((usr = calloc(1, sizeof(*usr))) == NULL) {
+               log_warn("could not allocate user");
+               return (NULL);
+       }
+
+       usr->usr_id.uid = uid;
+       usr->usr_id.gid = -1;
+       TAILQ_INSERT_TAIL(env->vmd_users, usr, usr_entry);
+
+ done:
+       DPRINTF("%s: uid %d #%d +",
+           __func__, usr->usr_id.uid, usr->usr_refcnt + 1);
+       usr->usr_refcnt++;
+
+       return (usr);
+}
+
+void
+user_put(struct vmd_user *usr)
+{
+       if (usr == NULL)
+               return;
+
+       DPRINTF("%s: uid %d #%d -",
+           __func__, usr->usr_id.uid, usr->usr_refcnt - 1);
+
+       if (--usr->usr_refcnt > 0)
+               return;
+
+       TAILQ_REMOVE(env->vmd_users, usr, usr_entry);
+       free(usr);
+}
+
+void
+user_inc(struct vm_create_params *vcp, struct vmd_user *usr, int inc)
+{
+       char     mem[FMT_SCALED_STRSIZE];
+
+       if (usr == NULL)
+               return;
+
+       /* increment or decrement counters */
+       inc = inc ? 1 : -1;
+
+       usr->usr_maxcpu += vcp->vcp_ncpus * inc;
+       usr->usr_maxmem += vcp->vcp_memranges[0].vmr_size * inc;
+       usr->usr_maxifs += vcp->vcp_nnics * inc;
+
+       if (log_getverbose() > 1) {
+               (void)fmt_scaled(usr->usr_maxmem * 1024 * 1024, mem);
+               log_debug("%s: %c uid %d ref %d cpu %llu mem %s ifs %llu",
+                   __func__, inc == 1 ? '+' : '-',
+                   usr->usr_id.uid, usr->usr_refcnt,
+                   usr->usr_maxcpu, mem, usr->usr_maxifs);
+       }
+}
+
+int
+user_checklimit(struct vmd_user *usr, struct vm_create_params *vcp)
+{
+       const char      *limit = "";
+
+       /* XXX make the limits configurable */
+       if (usr->usr_maxcpu > VM_DEFAULT_USER_MAXCPU) {
+               limit = "cpu ";
+               goto fail;
+       }
+       if (usr->usr_maxcpu > VM_DEFAULT_USER_MAXMEM) {
+               limit = "memory ";
+               goto fail;
+       }
+       if (usr->usr_maxifs > VM_DEFAULT_USER_MAXIFS) {
+               limit = "interface ";
+               goto fail;
+       }
+
+       return (0);
+
+ fail:
+       log_warnx("%s: user %d %slimit reached", vcp->vcp_name,
+           usr->usr_id.uid, limit);
+       return (-1);
+}
+
 char *
 get_string(uint8_t *ptr, size_t len)
 {
index bf670f5..2b83bb4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmd.h,v 1.77 2018/07/13 10:26:57 reyk Exp $   */
+/*     $OpenBSD: vmd.h,v 1.78 2018/07/15 14:36:54 reyk Exp $   */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
 #define VMD_SWITCH_TYPE                "bridge"
 #define VM_DEFAULT_MEMORY      512
 
+/* default user instance limits */
+#define VM_DEFAULT_USER_MAXCPU 4
+#define VM_DEFAULT_USER_MAXMEM 2048
+#define VM_DEFAULT_USER_MAXIFS 8
+
 /* vmd -> vmctl error codes */
 #define VMD_BIOS_MISSING       1001
 #define VMD_DISK_MISSING       1002
@@ -243,11 +248,23 @@ struct vmd_vm {
        int                      vm_received;
        int                      vm_paused;
        int                      vm_receive_fd;
+       struct vmd_user         *vm_user;
 
        TAILQ_ENTRY(vmd_vm)      vm_entry;
 };
 TAILQ_HEAD(vmlist, vmd_vm);
 
+struct vmd_user {
+       struct vmop_owner        usr_id;
+       uint64_t                 usr_maxcpu;
+       uint64_t                 usr_maxmem;
+       uint64_t                 usr_maxifs;
+       int                      usr_refcnt;
+
+       TAILQ_ENTRY(vmd_user)    usr_entry;
+};
+TAILQ_HEAD(userlist, vmd_user);
+
 struct address {
        struct sockaddr_storage  ss;
        int                      prefixlen;
@@ -274,6 +291,7 @@ struct vmd {
        struct vmlist           *vmd_vms;
        uint32_t                 vmd_nswitches;
        struct switchlist       *vmd_switches;
+       struct userlist         *vmd_users;
 
        int                      vmd_fd;
        int                      vmd_ptmfd;
@@ -329,6 +347,10 @@ int         vm_opentty(struct vmd_vm *);
 void    vm_closetty(struct vmd_vm *);
 void    switch_remove(struct vmd_switch *);
 struct vmd_switch *switch_getbyname(const char *);
+struct vmd_user *user_get(uid_t);
+void    user_put(struct vmd_user *);
+void    user_inc(struct vm_create_params *, struct vmd_user *, int);
+int     user_checklimit(struct vmd_user *, struct vm_create_params *);
 char   *get_string(uint8_t *, size_t);
 uint32_t prefixlen2mask(uint8_t);