Import of kernel-6.12.0-211.22.1.el10_2
This commit is contained in:
parent
c23a26575f
commit
3d57f395f9
@ -219,6 +219,7 @@ config ARM64
|
||||
if DYNAMIC_FTRACE_WITH_ARGS
|
||||
select HAVE_SAMPLE_FTRACE_DIRECT
|
||||
select HAVE_SAMPLE_FTRACE_DIRECT_MULTI
|
||||
select HAVE_BUILDTIME_MCOUNT_SORT
|
||||
select HAVE_EFFICIENT_UNALIGNED_ACCESS
|
||||
select HAVE_GUP_FAST
|
||||
select HAVE_FTRACE_MCOUNT_RECORD
|
||||
|
||||
@ -187,7 +187,7 @@ static pci_ers_result_t zpci_event_attempt_error_recovery(struct pci_dev *pdev)
|
||||
* is unbound or probed and that userspace can't access its
|
||||
* configuration space while we perform recovery.
|
||||
*/
|
||||
pci_dev_lock(pdev);
|
||||
device_lock(&pdev->dev);
|
||||
if (pdev->error_state == pci_channel_io_perm_failure) {
|
||||
ers_res = PCI_ERS_RESULT_DISCONNECT;
|
||||
goto out_unlock;
|
||||
@ -254,7 +254,7 @@ static pci_ers_result_t zpci_event_attempt_error_recovery(struct pci_dev *pdev)
|
||||
if (driver->err_handler->resume)
|
||||
driver->err_handler->resume(pdev);
|
||||
out_unlock:
|
||||
pci_dev_unlock(pdev);
|
||||
device_unlock(&pdev->dev);
|
||||
zpci_report_status(zdev, "recovery", status_str);
|
||||
|
||||
return ers_res;
|
||||
|
||||
@ -9076,6 +9076,8 @@ CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS=y
|
||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
||||
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
|
||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
||||
CONFIG_HAVE_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_TRACER_MAX_TRACE=y
|
||||
CONFIG_TRACE_CLOCK=y
|
||||
CONFIG_RING_BUFFER=y
|
||||
@ -9128,6 +9130,7 @@ CONFIG_RING_BUFFER_BENCHMARK=m
|
||||
# CONFIG_FTRACE_RECORD_RECURSION is not set
|
||||
# CONFIG_FTRACE_VALIDATE_RCU_IS_WATCHING is not set
|
||||
# CONFIG_FTRACE_STARTUP_TEST is not set
|
||||
# CONFIG_FTRACE_SORT_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set
|
||||
# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
|
||||
|
||||
@ -9022,6 +9022,8 @@ CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS=y
|
||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
||||
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
|
||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
||||
CONFIG_HAVE_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_TRACER_MAX_TRACE=y
|
||||
CONFIG_TRACE_CLOCK=y
|
||||
CONFIG_RING_BUFFER=y
|
||||
@ -9073,6 +9075,7 @@ CONFIG_RING_BUFFER_BENCHMARK=m
|
||||
# CONFIG_FTRACE_RECORD_RECURSION is not set
|
||||
# CONFIG_FTRACE_VALIDATE_RCU_IS_WATCHING is not set
|
||||
# CONFIG_FTRACE_STARTUP_TEST is not set
|
||||
# CONFIG_FTRACE_SORT_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set
|
||||
# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
|
||||
|
||||
@ -9084,6 +9084,8 @@ CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS=y
|
||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
||||
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
|
||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
||||
CONFIG_HAVE_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_TRACER_MAX_TRACE=y
|
||||
CONFIG_TRACE_CLOCK=y
|
||||
CONFIG_RING_BUFFER=y
|
||||
@ -9136,6 +9138,7 @@ CONFIG_RING_BUFFER_BENCHMARK=m
|
||||
# CONFIG_FTRACE_RECORD_RECURSION is not set
|
||||
# CONFIG_FTRACE_VALIDATE_RCU_IS_WATCHING is not set
|
||||
# CONFIG_FTRACE_STARTUP_TEST is not set
|
||||
# CONFIG_FTRACE_SORT_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set
|
||||
# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
|
||||
|
||||
@ -9055,6 +9055,8 @@ CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS=y
|
||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
||||
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
|
||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
||||
CONFIG_HAVE_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_TRACER_MAX_TRACE=y
|
||||
CONFIG_TRACE_CLOCK=y
|
||||
CONFIG_RING_BUFFER=y
|
||||
@ -9107,6 +9109,7 @@ CONFIG_RING_BUFFER_BENCHMARK=m
|
||||
# CONFIG_FTRACE_RECORD_RECURSION is not set
|
||||
# CONFIG_FTRACE_VALIDATE_RCU_IS_WATCHING is not set
|
||||
# CONFIG_FTRACE_STARTUP_TEST is not set
|
||||
# CONFIG_FTRACE_SORT_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set
|
||||
# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
|
||||
|
||||
@ -9001,6 +9001,8 @@ CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS=y
|
||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
||||
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
|
||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
||||
CONFIG_HAVE_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_TRACER_MAX_TRACE=y
|
||||
CONFIG_TRACE_CLOCK=y
|
||||
CONFIG_RING_BUFFER=y
|
||||
@ -9052,6 +9054,7 @@ CONFIG_RING_BUFFER_BENCHMARK=m
|
||||
# CONFIG_FTRACE_RECORD_RECURSION is not set
|
||||
# CONFIG_FTRACE_VALIDATE_RCU_IS_WATCHING is not set
|
||||
# CONFIG_FTRACE_STARTUP_TEST is not set
|
||||
# CONFIG_FTRACE_SORT_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set
|
||||
# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
|
||||
|
||||
@ -9062,6 +9062,8 @@ CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS=y
|
||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
||||
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
|
||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
||||
CONFIG_HAVE_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_TRACER_MAX_TRACE=y
|
||||
CONFIG_TRACE_CLOCK=y
|
||||
CONFIG_RING_BUFFER=y
|
||||
@ -9114,6 +9116,7 @@ CONFIG_RING_BUFFER_BENCHMARK=m
|
||||
# CONFIG_FTRACE_RECORD_RECURSION is not set
|
||||
# CONFIG_FTRACE_VALIDATE_RCU_IS_WATCHING is not set
|
||||
# CONFIG_FTRACE_STARTUP_TEST is not set
|
||||
# CONFIG_FTRACE_SORT_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set
|
||||
# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
|
||||
|
||||
@ -9008,6 +9008,8 @@ CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS=y
|
||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
||||
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
|
||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
||||
CONFIG_HAVE_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_TRACER_MAX_TRACE=y
|
||||
CONFIG_TRACE_CLOCK=y
|
||||
CONFIG_RING_BUFFER=y
|
||||
@ -9059,6 +9061,7 @@ CONFIG_RING_BUFFER_BENCHMARK=m
|
||||
# CONFIG_FTRACE_RECORD_RECURSION is not set
|
||||
# CONFIG_FTRACE_VALIDATE_RCU_IS_WATCHING is not set
|
||||
# CONFIG_FTRACE_STARTUP_TEST is not set
|
||||
# CONFIG_FTRACE_SORT_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set
|
||||
# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
|
||||
|
||||
@ -9030,6 +9030,8 @@ CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS=y
|
||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
||||
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
|
||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
||||
CONFIG_HAVE_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_TRACER_MAX_TRACE=y
|
||||
CONFIG_TRACE_CLOCK=y
|
||||
CONFIG_RING_BUFFER=y
|
||||
@ -9081,6 +9083,7 @@ CONFIG_RING_BUFFER_BENCHMARK=m
|
||||
# CONFIG_FTRACE_RECORD_RECURSION is not set
|
||||
# CONFIG_FTRACE_VALIDATE_RCU_IS_WATCHING is not set
|
||||
# CONFIG_FTRACE_STARTUP_TEST is not set
|
||||
# CONFIG_FTRACE_SORT_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_STARTUP_TEST is not set
|
||||
# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set
|
||||
# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
|
||||
|
||||
@ -737,8 +737,8 @@ r535_gsp_acpi_caps(acpi_handle handle, CAPS_METHOD_DATA *caps)
|
||||
if (!obj)
|
||||
goto done;
|
||||
|
||||
if (WARN_ON(obj->type != ACPI_TYPE_BUFFER) ||
|
||||
WARN_ON(obj->buffer.length != 4))
|
||||
if (obj->type != ACPI_TYPE_BUFFER ||
|
||||
obj->buffer.length != 4)
|
||||
goto done;
|
||||
|
||||
caps->status = 0;
|
||||
@ -773,8 +773,8 @@ r535_gsp_acpi_jt(acpi_handle handle, JT_METHOD_DATA *jt)
|
||||
if (!obj)
|
||||
goto done;
|
||||
|
||||
if (WARN_ON(obj->type != ACPI_TYPE_BUFFER) ||
|
||||
WARN_ON(obj->buffer.length != 4))
|
||||
if (obj->type != ACPI_TYPE_BUFFER ||
|
||||
obj->buffer.length != 4)
|
||||
goto done;
|
||||
|
||||
jt->status = 0;
|
||||
@ -861,8 +861,8 @@ r535_gsp_acpi_dod(acpi_handle handle, DOD_METHOD_DATA *dod)
|
||||
|
||||
_DOD = output.pointer;
|
||||
|
||||
if (WARN_ON(_DOD->type != ACPI_TYPE_PACKAGE) ||
|
||||
WARN_ON(_DOD->package.count > ARRAY_SIZE(dod->acpiIdList)))
|
||||
if (_DOD->type != ACPI_TYPE_PACKAGE ||
|
||||
_DOD->package.count > ARRAY_SIZE(dod->acpiIdList))
|
||||
return;
|
||||
|
||||
for (int i = 0; i < _DOD->package.count; i++) {
|
||||
|
||||
@ -5401,7 +5401,7 @@ static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb,
|
||||
if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP))
|
||||
continue;
|
||||
|
||||
if (bond_is_last_slave(bond, slave)) {
|
||||
if (i + 1 == slaves_count) {
|
||||
skb2 = skb;
|
||||
skb_used = true;
|
||||
} else {
|
||||
|
||||
@ -3015,7 +3015,7 @@ ice_vsi_rebuild_set_coalesce(struct ice_vsi *vsi,
|
||||
* ice_vsi_realloc_stat_arrays - Frees unused stat structures or alloc new ones
|
||||
* @vsi: VSI pointer
|
||||
*/
|
||||
static int
|
||||
int
|
||||
ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi)
|
||||
{
|
||||
u16 req_txq = vsi->req_txq ? vsi->req_txq : vsi->alloc_txq;
|
||||
|
||||
@ -66,6 +66,7 @@ int ice_ena_vsi(struct ice_vsi *vsi, bool locked);
|
||||
void ice_vsi_decfg(struct ice_vsi *vsi);
|
||||
void ice_dis_vsi(struct ice_vsi *vsi, bool locked);
|
||||
|
||||
int ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi);
|
||||
int ice_vsi_rebuild(struct ice_vsi *vsi, u32 vsi_flags);
|
||||
int ice_vsi_cfg(struct ice_vsi *vsi);
|
||||
struct ice_vsi *ice_vsi_alloc(struct ice_pf *pf);
|
||||
|
||||
@ -268,6 +268,13 @@ static int ice_vf_reconfig_vsi(struct ice_vf *vf)
|
||||
|
||||
vsi->flags = ICE_VSI_FLAG_NO_INIT;
|
||||
|
||||
vsi->req_txq = vf->num_req_qs;
|
||||
vsi->req_rxq = vf->num_req_qs;
|
||||
|
||||
err = ice_vsi_realloc_stat_arrays(vsi);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ice_vsi_decfg(vsi);
|
||||
ice_fltr_remove_all(vsi);
|
||||
|
||||
|
||||
@ -6145,10 +6145,12 @@ static void copy_pair_set_active(struct dasd_copy_relation *copy, char *new_busi
|
||||
static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid,
|
||||
char *sec_busid)
|
||||
{
|
||||
struct dasd_eckd_private *prim_priv, *sec_priv;
|
||||
struct dasd_device *primary, *secondary;
|
||||
struct dasd_copy_relation *copy;
|
||||
struct dasd_block *block;
|
||||
struct gendisk *gdp;
|
||||
int rc;
|
||||
|
||||
copy = device->copy;
|
||||
if (!copy)
|
||||
@ -6164,6 +6166,9 @@ static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid
|
||||
if (!secondary)
|
||||
return DASD_COPYPAIRSWAP_SECONDARY;
|
||||
|
||||
prim_priv = primary->private;
|
||||
sec_priv = secondary->private;
|
||||
|
||||
/*
|
||||
* usually the device should be quiesced for swap
|
||||
* for paranoia stop device and requeue requests again
|
||||
@ -6183,6 +6188,25 @@ static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid
|
||||
/* swap blocklayer device link */
|
||||
gdp = block->gdp;
|
||||
dasd_add_link_to_gendisk(gdp, secondary);
|
||||
rc = device_move(disk_to_dev(gdp), &secondary->cdev->dev, DPM_ORDER_NONE);
|
||||
if (rc) {
|
||||
dev_err(&primary->cdev->dev,
|
||||
"copy_pair_swap: moving blockdevice parent %s->%s failed (%d)\n",
|
||||
dev_name(&primary->cdev->dev),
|
||||
dev_name(&secondary->cdev->dev), rc);
|
||||
}
|
||||
|
||||
if (primary->stopped & DASD_STOPPED_QUIESCE) {
|
||||
dasd_device_set_stop_bits(secondary, DASD_STOPPED_QUIESCE);
|
||||
dasd_device_remove_stop_bits(primary, DASD_STOPPED_QUIESCE);
|
||||
}
|
||||
|
||||
/*
|
||||
* The secondary device never got through format detection, but since it
|
||||
* is a copy of the primary device, the format is exactly the same;
|
||||
* therefore, the detected layout can simply be copied.
|
||||
*/
|
||||
sec_priv->uses_cdl = prim_priv->uses_cdl;
|
||||
|
||||
/* re-enable device */
|
||||
dasd_device_remove_stop_bits(primary, DASD_STOPPED_PPRC);
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include <linux/backing-file.h>
|
||||
#include <linux/splice.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/security.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@ -29,19 +30,20 @@
|
||||
* returned file into a container structure that also stores the stacked
|
||||
* file's path, which can be retrieved using backing_file_user_path().
|
||||
*/
|
||||
struct file *backing_file_open(const struct path *user_path, int flags,
|
||||
struct file *backing_file_open(const struct file *user_file, int flags,
|
||||
const struct path *real_path,
|
||||
const struct cred *cred)
|
||||
{
|
||||
const struct path *user_path = &user_file->f_path;
|
||||
struct file *f;
|
||||
int error;
|
||||
|
||||
f = alloc_empty_backing_file(flags, cred);
|
||||
f = alloc_empty_backing_file(flags, cred, user_file);
|
||||
if (IS_ERR(f))
|
||||
return f;
|
||||
|
||||
path_get(user_path);
|
||||
*backing_file_user_path(f) = *user_path;
|
||||
backing_file_set_user_path(f, user_path);
|
||||
error = vfs_open(real_path, f);
|
||||
if (error) {
|
||||
fput(f);
|
||||
@ -52,20 +54,21 @@ struct file *backing_file_open(const struct path *user_path, int flags,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(backing_file_open);
|
||||
|
||||
struct file *backing_tmpfile_open(const struct path *user_path, int flags,
|
||||
struct file *backing_tmpfile_open(const struct file *user_file, int flags,
|
||||
const struct path *real_parentpath,
|
||||
umode_t mode, const struct cred *cred)
|
||||
{
|
||||
struct mnt_idmap *real_idmap = mnt_idmap(real_parentpath->mnt);
|
||||
const struct path *user_path = &user_file->f_path;
|
||||
struct file *f;
|
||||
int error;
|
||||
|
||||
f = alloc_empty_backing_file(flags, cred);
|
||||
f = alloc_empty_backing_file(flags, cred, user_file);
|
||||
if (IS_ERR(f))
|
||||
return f;
|
||||
|
||||
path_get(user_path);
|
||||
*backing_file_user_path(f) = *user_path;
|
||||
backing_file_set_user_path(f, user_path);
|
||||
error = vfs_tmpfile(real_idmap, real_parentpath, f, mode);
|
||||
if (error) {
|
||||
fput(f);
|
||||
@ -339,6 +342,11 @@ int backing_file_mmap(struct file *file, struct vm_area_struct *vma,
|
||||
vma_set_file(vma, file);
|
||||
|
||||
old_cred = override_creds(ctx->cred);
|
||||
ret = security_mmap_backing_file(vma, file, ctx->user_file);
|
||||
if (ret) {
|
||||
revert_creds(old_cred);
|
||||
return ret;
|
||||
}
|
||||
ret = call_mmap(vma->vm_file, vma);
|
||||
revert_creds(old_cred);
|
||||
|
||||
|
||||
@ -1337,6 +1337,7 @@ static void z_erofs_decompress_kickoff(struct z_erofs_decompressqueue *io,
|
||||
int bios)
|
||||
{
|
||||
struct erofs_sb_info *const sbi = EROFS_SB(io->sb);
|
||||
int gfp_flag;
|
||||
|
||||
/* wake up the caller thread for sync decompression */
|
||||
if (io->sync) {
|
||||
@ -1370,7 +1371,9 @@ static void z_erofs_decompress_kickoff(struct z_erofs_decompressqueue *io,
|
||||
sbi->opt.sync_decompress = EROFS_SYNC_DECOMPRESS_FORCE_ON;
|
||||
return;
|
||||
}
|
||||
gfp_flag = memalloc_noio_save();
|
||||
z_erofs_decompressqueue_work(&io->u.work);
|
||||
memalloc_noio_restore(gfp_flag);
|
||||
}
|
||||
|
||||
static void z_erofs_fill_bio_vec(struct bio_vec *bvec,
|
||||
|
||||
@ -51,19 +51,44 @@ struct backing_file {
|
||||
struct path user_path;
|
||||
freeptr_t bf_freeptr;
|
||||
};
|
||||
#ifdef CONFIG_SECURITY
|
||||
void *security;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline struct backing_file *backing_file(struct file *f)
|
||||
{
|
||||
return container_of(f, struct backing_file, file);
|
||||
}
|
||||
#define backing_file(f) container_of(f, struct backing_file, file)
|
||||
|
||||
struct path *backing_file_user_path(struct file *f)
|
||||
struct path *backing_file_user_path(const struct file *f)
|
||||
{
|
||||
return &backing_file(f)->user_path;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(backing_file_user_path);
|
||||
|
||||
void backing_file_set_user_path(struct file *f, const struct path *path)
|
||||
{
|
||||
backing_file(f)->user_path = *path;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(backing_file_set_user_path);
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
void *backing_file_security(const struct file *f)
|
||||
{
|
||||
return backing_file(f)->security;
|
||||
}
|
||||
|
||||
void backing_file_set_security(struct file *f, void *security)
|
||||
{
|
||||
backing_file(f)->security = security;
|
||||
}
|
||||
#endif /* CONFIG_SECURITY */
|
||||
|
||||
static inline void backing_file_free(struct backing_file *ff)
|
||||
{
|
||||
security_backing_file_free(&ff->file);
|
||||
path_put(&ff->user_path);
|
||||
kmem_cache_free(bfilp_cachep, ff);
|
||||
}
|
||||
|
||||
static inline void file_free(struct file *f)
|
||||
{
|
||||
security_file_free(f);
|
||||
@ -71,8 +96,7 @@ static inline void file_free(struct file *f)
|
||||
percpu_counter_dec(&nr_files);
|
||||
put_cred(f->f_cred);
|
||||
if (unlikely(f->f_mode & FMODE_BACKING)) {
|
||||
path_put(backing_file_user_path(f));
|
||||
kmem_cache_free(bfilp_cachep, backing_file(f));
|
||||
backing_file_free(backing_file(f));
|
||||
} else {
|
||||
kmem_cache_free(filp_cachep, f);
|
||||
}
|
||||
@ -275,6 +299,14 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
|
||||
return f;
|
||||
}
|
||||
|
||||
static int init_backing_file(struct backing_file *ff,
|
||||
const struct file *user_file)
|
||||
{
|
||||
memset(&ff->user_path, 0, sizeof(ff->user_path));
|
||||
backing_file_set_security(&ff->file, NULL);
|
||||
return security_backing_file_alloc(&ff->file, user_file);
|
||||
}
|
||||
|
||||
/*
|
||||
* Variant of alloc_empty_file() that allocates a backing_file container
|
||||
* and doesn't check and modify nr_files.
|
||||
@ -282,7 +314,8 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
|
||||
* This is only for kernel internal use, and the allocate file must not be
|
||||
* installed into file tables or such.
|
||||
*/
|
||||
struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
|
||||
struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
|
||||
const struct file *user_file)
|
||||
{
|
||||
struct backing_file *ff;
|
||||
int error;
|
||||
@ -297,7 +330,14 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred)
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
/* The f_mode flags must be set before fput(). */
|
||||
ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT;
|
||||
error = init_backing_file(ff, user_file);
|
||||
if (unlikely(error)) {
|
||||
fput(&ff->file);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
return &ff->file;
|
||||
}
|
||||
|
||||
|
||||
@ -321,7 +321,7 @@ struct fuse_backing *fuse_passthrough_open(struct file *file,
|
||||
goto out;
|
||||
|
||||
/* Allocate backing file per fuse file to store fuse path */
|
||||
backing_file = backing_file_open(&file->f_path, file->f_flags,
|
||||
backing_file = backing_file_open(file, file->f_flags,
|
||||
&fb->file->f_path, fb->cred);
|
||||
err = PTR_ERR(backing_file);
|
||||
if (IS_ERR(backing_file)) {
|
||||
|
||||
@ -99,7 +99,9 @@ extern void chroot_fs_refs(const struct path *, const struct path *);
|
||||
*/
|
||||
struct file *alloc_empty_file(int flags, const struct cred *cred);
|
||||
struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred);
|
||||
struct file *alloc_empty_backing_file(int flags, const struct cred *cred);
|
||||
struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
|
||||
const struct file *user_file);
|
||||
void backing_file_set_user_path(struct file *f, const struct path *path);
|
||||
|
||||
static inline void file_put_write_access(struct file *file)
|
||||
{
|
||||
|
||||
@ -1320,7 +1320,7 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
|
||||
goto out_revert_creds;
|
||||
|
||||
ovl_path_upper(dentry->d_parent, &realparentpath);
|
||||
realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath,
|
||||
realfile = backing_tmpfile_open(file, flags, &realparentpath,
|
||||
mode, current_cred());
|
||||
err = PTR_ERR_OR_ZERO(realfile);
|
||||
pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err);
|
||||
|
||||
@ -48,7 +48,7 @@ static struct file *ovl_open_realfile(const struct file *file,
|
||||
if (!inode_owner_or_capable(real_idmap, realinode))
|
||||
flags &= ~O_NOATIME;
|
||||
|
||||
realfile = backing_file_open(&file->f_path, flags, realpath,
|
||||
realfile = backing_file_open(file, flags, realpath,
|
||||
current_cred());
|
||||
}
|
||||
revert_creds(old_cred);
|
||||
|
||||
0
include/config/BUILDTIME_MCOUNT_SORT
Normal file
0
include/config/BUILDTIME_MCOUNT_SORT
Normal file
0
include/config/HAVE_BUILDTIME_MCOUNT_SORT
Normal file
0
include/config/HAVE_BUILDTIME_MCOUNT_SORT
Normal file
@ -2240,6 +2240,7 @@ CONFIG_NLS_MAC_CYRILLIC=m
|
||||
CONFIG_BT_BCM=m
|
||||
CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
|
||||
CONFIG_HARDENED_USERCOPY=y
|
||||
CONFIG_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_NETFILTER_XT_TARGET_MARK=m
|
||||
CONFIG_PWM_TIEHRPWM=m
|
||||
CONFIG_HAS_IOMEM=y
|
||||
@ -4483,6 +4484,7 @@ CONFIG_SND_CTL_LED=m
|
||||
CONFIG_NUMA_BALANCING=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
|
||||
CONFIG_CLK_TEGRA_BPMP=y
|
||||
CONFIG_HAVE_BUILDTIME_MCOUNT_SORT=y
|
||||
CONFIG_FWCTL=m
|
||||
CONFIG_QUOTACTL=y
|
||||
CONFIG_BRIDGE_NF_EBTABLES=m
|
||||
|
||||
@ -2240,6 +2240,7 @@
|
||||
#define CONFIG_BT_BCM_MODULE 1
|
||||
#define CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC 1
|
||||
#define CONFIG_HARDENED_USERCOPY 1
|
||||
#define CONFIG_BUILDTIME_MCOUNT_SORT 1
|
||||
#define CONFIG_NETFILTER_XT_TARGET_MARK_MODULE 1
|
||||
#define CONFIG_PWM_TIEHRPWM_MODULE 1
|
||||
#define CONFIG_HAS_IOMEM 1
|
||||
@ -4483,6 +4484,7 @@
|
||||
#define CONFIG_NUMA_BALANCING 1
|
||||
#define CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE 1
|
||||
#define CONFIG_CLK_TEGRA_BPMP 1
|
||||
#define CONFIG_HAVE_BUILDTIME_MCOUNT_SORT 1
|
||||
#define CONFIG_FWCTL_MODULE 1
|
||||
#define CONFIG_QUOTACTL 1
|
||||
#define CONFIG_BRIDGE_NF_EBTABLES_MODULE 1
|
||||
|
||||
@ -4397,6 +4397,8 @@
|
||||
--cfg=CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC="y"
|
||||
--cfg=CONFIG_HARDENED_USERCOPY
|
||||
--cfg=CONFIG_HARDENED_USERCOPY="y"
|
||||
--cfg=CONFIG_BUILDTIME_MCOUNT_SORT
|
||||
--cfg=CONFIG_BUILDTIME_MCOUNT_SORT="y"
|
||||
--cfg=CONFIG_NETFILTER_XT_TARGET_MARK
|
||||
--cfg=CONFIG_NETFILTER_XT_TARGET_MARK="m"
|
||||
--cfg=CONFIG_PWM_TIEHRPWM
|
||||
@ -8820,6 +8822,8 @@
|
||||
--cfg=CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE="y"
|
||||
--cfg=CONFIG_CLK_TEGRA_BPMP
|
||||
--cfg=CONFIG_CLK_TEGRA_BPMP="y"
|
||||
--cfg=CONFIG_HAVE_BUILDTIME_MCOUNT_SORT
|
||||
--cfg=CONFIG_HAVE_BUILDTIME_MCOUNT_SORT="y"
|
||||
--cfg=CONFIG_FWCTL
|
||||
--cfg=CONFIG_FWCTL="m"
|
||||
--cfg=CONFIG_QUOTACTL
|
||||
|
||||
@ -19,10 +19,10 @@ struct backing_file_ctx {
|
||||
void (*end_write)(struct file *, loff_t, ssize_t);
|
||||
};
|
||||
|
||||
struct file *backing_file_open(const struct path *user_path, int flags,
|
||||
struct file *backing_file_open(const struct file *user_file, int flags,
|
||||
const struct path *real_path,
|
||||
const struct cred *cred);
|
||||
struct file *backing_tmpfile_open(const struct path *user_path, int flags,
|
||||
struct file *backing_tmpfile_open(const struct file *user_file, int flags,
|
||||
const struct path *real_parentpath,
|
||||
umode_t mode, const struct cred *cred);
|
||||
ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter,
|
||||
|
||||
@ -2790,7 +2790,20 @@ struct file *dentry_open(const struct path *path, int flags,
|
||||
const struct cred *creds);
|
||||
struct file *dentry_create(const struct path *path, int flags, umode_t mode,
|
||||
const struct cred *cred);
|
||||
struct path *backing_file_user_path(struct file *f);
|
||||
struct path *backing_file_user_path(const struct file *f);
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
void *backing_file_security(const struct file *f);
|
||||
void backing_file_set_security(struct file *f, void *security);
|
||||
#else
|
||||
static inline void *backing_file_security(const struct file *f)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline void backing_file_set_security(struct file *f, void *security)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_SECURITY */
|
||||
|
||||
/*
|
||||
* When mmapping a file on a stackable filesystem (e.g., overlayfs), the file
|
||||
@ -2802,14 +2815,14 @@ struct path *backing_file_user_path(struct file *f);
|
||||
* by fstat() on that same fd.
|
||||
*/
|
||||
/* Get the path to display in /proc/<pid>/maps */
|
||||
static inline const struct path *file_user_path(struct file *f)
|
||||
static inline const struct path *file_user_path(const struct file *f)
|
||||
{
|
||||
if (unlikely(f->f_mode & FMODE_BACKING))
|
||||
return backing_file_user_path(f);
|
||||
return &f->f_path;
|
||||
}
|
||||
/* Get the inode whose inode number to display in /proc/<pid>/maps */
|
||||
static inline const struct inode *file_user_inode(struct file *f)
|
||||
static inline const struct inode *file_user_inode(const struct file *f)
|
||||
{
|
||||
if (unlikely(f->f_mode & FMODE_BACKING))
|
||||
return d_inode(backing_file_user_path(f)->dentry);
|
||||
|
||||
@ -93,7 +93,7 @@ struct common_audit_data {
|
||||
#endif
|
||||
char *kmod_name;
|
||||
struct lsm_ioctlop_audit *op;
|
||||
struct file *file;
|
||||
const struct file *file;
|
||||
struct lsm_ibpkey_audit *ibpkey;
|
||||
struct lsm_ibendport_audit *ibendport;
|
||||
int reason;
|
||||
|
||||
@ -188,6 +188,9 @@ LSM_HOOK(int, 0, file_permission, struct file *file, int mask)
|
||||
LSM_HOOK(int, 0, file_alloc_security, struct file *file)
|
||||
LSM_HOOK(void, LSM_RET_VOID, file_release, struct file *file)
|
||||
LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file)
|
||||
LSM_HOOK(int, 0, backing_file_alloc, struct file *backing_file,
|
||||
const struct file *user_file)
|
||||
LSM_HOOK(void, LSM_RET_VOID, backing_file_free, struct file *backing_file)
|
||||
LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
|
||||
@ -195,6 +198,8 @@ LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
|
||||
LSM_HOOK(int, 0, mmap_addr, unsigned long addr)
|
||||
LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot,
|
||||
unsigned long prot, unsigned long flags)
|
||||
LSM_HOOK(int, 0, mmap_backing_file, struct vm_area_struct *vma,
|
||||
struct file *backing_file, struct file *user_file)
|
||||
LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma,
|
||||
unsigned long reqprot, unsigned long prot)
|
||||
LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd)
|
||||
|
||||
@ -102,20 +102,21 @@ struct security_hook_list {
|
||||
* Security blob size or offset data.
|
||||
*/
|
||||
struct lsm_blob_sizes {
|
||||
int lbs_cred;
|
||||
int lbs_file;
|
||||
int lbs_ib;
|
||||
int lbs_inode;
|
||||
int lbs_sock;
|
||||
int lbs_superblock;
|
||||
int lbs_ipc;
|
||||
int lbs_key;
|
||||
int lbs_msg_msg;
|
||||
int lbs_perf_event;
|
||||
int lbs_task;
|
||||
int lbs_xattr_count; /* number of xattr slots in new_xattrs array */
|
||||
int lbs_tun_dev;
|
||||
int lbs_bdev;
|
||||
unsigned int lbs_cred;
|
||||
unsigned int lbs_file;
|
||||
unsigned int lbs_backing_file;
|
||||
unsigned int lbs_ib;
|
||||
unsigned int lbs_inode;
|
||||
unsigned int lbs_sock;
|
||||
unsigned int lbs_superblock;
|
||||
unsigned int lbs_ipc;
|
||||
unsigned int lbs_key;
|
||||
unsigned int lbs_msg_msg;
|
||||
unsigned int lbs_perf_event;
|
||||
unsigned int lbs_task;
|
||||
unsigned int lbs_xattr_count; /* num xattr slots in new_xattrs array */
|
||||
unsigned int lbs_tun_dev;
|
||||
unsigned int lbs_bdev;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -149,7 +150,7 @@ enum lsm_order {
|
||||
};
|
||||
|
||||
struct lsm_info {
|
||||
const char *name; /* Required. */
|
||||
const struct lsm_id *id;
|
||||
enum lsm_order order; /* Optional: default is LSM_ORDER_MUTABLE */
|
||||
unsigned long flags; /* Optional: flags describing LSM */
|
||||
int *enabled; /* Optional: controlled by CONFIG_LSM */
|
||||
@ -167,11 +168,9 @@ struct lsm_info {
|
||||
__used __section(".early_lsm_info.init") \
|
||||
__aligned(sizeof(unsigned long))
|
||||
|
||||
|
||||
/* DO NOT tamper with these variables outside of the LSM framework */
|
||||
extern char *lsm_names;
|
||||
extern struct lsm_static_calls_table static_calls_table __ro_after_init;
|
||||
extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
|
||||
extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
|
||||
|
||||
/**
|
||||
* lsm_get_xattr_slot - Return the next available slot and increment the index
|
||||
|
||||
@ -153,8 +153,6 @@ enum lockdown_reason {
|
||||
};
|
||||
|
||||
extern const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1];
|
||||
extern u32 lsm_active_cnt;
|
||||
extern const struct lsm_id *lsm_idlist[];
|
||||
|
||||
/* These functions are in security/commoncap.c */
|
||||
extern int cap_capable(const struct cred *cred, struct user_namespace *ns,
|
||||
@ -420,11 +418,17 @@ int security_file_permission(struct file *file, int mask);
|
||||
int security_file_alloc(struct file *file);
|
||||
void security_file_release(struct file *file);
|
||||
void security_file_free(struct file *file);
|
||||
int security_backing_file_alloc(struct file *backing_file,
|
||||
const struct file *user_file);
|
||||
void security_backing_file_free(struct file *backing_file);
|
||||
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
int security_file_ioctl_compat(struct file *file, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
int security_mmap_file(struct file *file, unsigned long prot,
|
||||
unsigned long flags);
|
||||
int security_mmap_backing_file(struct vm_area_struct *vma,
|
||||
struct file *backing_file,
|
||||
struct file *user_file);
|
||||
int security_mmap_addr(unsigned long addr);
|
||||
int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
|
||||
unsigned long prot);
|
||||
@ -1065,6 +1069,15 @@ static inline void security_file_release(struct file *file)
|
||||
static inline void security_file_free(struct file *file)
|
||||
{ }
|
||||
|
||||
static inline int security_backing_file_alloc(struct file *backing_file,
|
||||
const struct file *user_file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void security_backing_file_free(struct file *backing_file)
|
||||
{ }
|
||||
|
||||
static inline int security_file_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
@ -1084,6 +1097,13 @@ static inline int security_mmap_file(struct file *file, unsigned long prot,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_mmap_backing_file(struct vm_area_struct *vma,
|
||||
struct file *backing_file,
|
||||
struct file *user_file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_mmap_addr(unsigned long addr)
|
||||
{
|
||||
return cap_mmap_addr(addr);
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
|
||||
kernel.almalinux,1,AlmaLinux,kernel-core,6.12.0-211.20.1.el10.x86_64,mailto:security@almalinux.org
|
||||
kernel.almalinux,1,AlmaLinux,kernel-core,6.12.0-211.22.1.el10.x86_64,mailto:security@almalinux.org
|
||||
|
||||
@ -1122,7 +1122,6 @@ struct ftrace_page {
|
||||
};
|
||||
|
||||
#define ENTRY_SIZE sizeof(struct dyn_ftrace)
|
||||
#define ENTRIES_PER_PAGE (PAGE_SIZE / ENTRY_SIZE)
|
||||
|
||||
static struct ftrace_page *ftrace_pages_start;
|
||||
static struct ftrace_page *ftrace_pages;
|
||||
@ -3741,7 +3740,8 @@ static int ftrace_update_code(struct module *mod, struct ftrace_page *new_pgs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftrace_allocate_records(struct ftrace_page *pg, int count)
|
||||
static int ftrace_allocate_records(struct ftrace_page *pg, int count,
|
||||
unsigned long *num_pages)
|
||||
{
|
||||
int order;
|
||||
int pages;
|
||||
@ -3751,7 +3751,7 @@ static int ftrace_allocate_records(struct ftrace_page *pg, int count)
|
||||
return -EINVAL;
|
||||
|
||||
/* We want to fill as much as possible, with no empty pages */
|
||||
pages = DIV_ROUND_UP(count, ENTRIES_PER_PAGE);
|
||||
pages = DIV_ROUND_UP(count * ENTRY_SIZE, PAGE_SIZE);
|
||||
order = fls(pages) - 1;
|
||||
|
||||
again:
|
||||
@ -3766,6 +3766,7 @@ static int ftrace_allocate_records(struct ftrace_page *pg, int count)
|
||||
}
|
||||
|
||||
ftrace_number_of_pages += 1 << order;
|
||||
*num_pages += 1 << order;
|
||||
ftrace_number_of_groups++;
|
||||
|
||||
cnt = (PAGE_SIZE << order) / ENTRY_SIZE;
|
||||
@ -3794,12 +3795,14 @@ static void ftrace_free_pages(struct ftrace_page *pages)
|
||||
}
|
||||
|
||||
static struct ftrace_page *
|
||||
ftrace_allocate_pages(unsigned long num_to_init)
|
||||
ftrace_allocate_pages(unsigned long num_to_init, unsigned long *num_pages)
|
||||
{
|
||||
struct ftrace_page *start_pg;
|
||||
struct ftrace_page *pg;
|
||||
int cnt;
|
||||
|
||||
*num_pages = 0;
|
||||
|
||||
if (!num_to_init)
|
||||
return NULL;
|
||||
|
||||
@ -3813,7 +3816,7 @@ ftrace_allocate_pages(unsigned long num_to_init)
|
||||
* waste as little space as possible.
|
||||
*/
|
||||
for (;;) {
|
||||
cnt = ftrace_allocate_records(pg, num_to_init);
|
||||
cnt = ftrace_allocate_records(pg, num_to_init, num_pages);
|
||||
if (cnt < 0)
|
||||
goto free_pages;
|
||||
|
||||
@ -7019,6 +7022,7 @@ static int ftrace_process_locs(struct module *mod,
|
||||
unsigned long *p;
|
||||
unsigned long addr;
|
||||
unsigned long flags = 0; /* Shut up gcc */
|
||||
unsigned long pages;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
count = end - start;
|
||||
@ -7038,7 +7042,7 @@ static int ftrace_process_locs(struct module *mod,
|
||||
test_is_sorted(start, count);
|
||||
}
|
||||
|
||||
start_pg = ftrace_allocate_pages(count);
|
||||
start_pg = ftrace_allocate_pages(count, &pages);
|
||||
if (!start_pg)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -7070,7 +7074,9 @@ static int ftrace_process_locs(struct module *mod,
|
||||
pg = start_pg;
|
||||
while (p < end) {
|
||||
unsigned long end_offset;
|
||||
addr = ftrace_call_adjust(*p++);
|
||||
|
||||
addr = *p++;
|
||||
|
||||
/*
|
||||
* Some architecture linkers will pad between
|
||||
* the different mcount_loc sections of different
|
||||
@ -7082,6 +7088,19 @@ static int ftrace_process_locs(struct module *mod,
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is core kernel, make sure the address is in core
|
||||
* or inittext, as weak functions get zeroed and KASLR can
|
||||
* move them to something other than zero. It just will not
|
||||
* move it to an area where kernel text is.
|
||||
*/
|
||||
if (!mod && !(is_kernel_text(addr) || is_kernel_inittext(addr))) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
addr = ftrace_call_adjust(addr);
|
||||
|
||||
end_offset = (pg->index+1) * sizeof(pg->records[0]);
|
||||
if (end_offset > PAGE_SIZE << pg->order) {
|
||||
/* We should have allocated enough */
|
||||
@ -7121,11 +7140,41 @@ static int ftrace_process_locs(struct module *mod,
|
||||
|
||||
/* We should have used all pages unless we skipped some */
|
||||
if (pg_unuse) {
|
||||
WARN_ON(!skipped);
|
||||
unsigned long pg_remaining, remaining = 0;
|
||||
long skip;
|
||||
|
||||
/* Count the number of entries unused and compare it to skipped. */
|
||||
pg_remaining = (PAGE_SIZE << pg->order) / ENTRY_SIZE - pg->index;
|
||||
|
||||
if (!WARN(skipped < pg_remaining, "Extra allocated pages for ftrace")) {
|
||||
|
||||
skip = skipped - pg_remaining;
|
||||
|
||||
for (pg = pg_unuse; pg && skip > 0; pg = pg->next) {
|
||||
remaining += 1 << pg->order;
|
||||
skip -= (PAGE_SIZE << pg->order) / ENTRY_SIZE;
|
||||
}
|
||||
|
||||
pages -= remaining;
|
||||
|
||||
/*
|
||||
* Check to see if the number of pages remaining would
|
||||
* just fit the number of entries skipped.
|
||||
*/
|
||||
WARN(pg || skip > 0, "Extra allocated pages for ftrace: %lu with %lu skipped",
|
||||
remaining, skipped);
|
||||
}
|
||||
/* Need to synchronize with ftrace_location_range() */
|
||||
synchronize_rcu();
|
||||
ftrace_free_pages(pg_unuse);
|
||||
}
|
||||
|
||||
if (!mod) {
|
||||
count -= skipped;
|
||||
pr_info("ftrace: allocating %ld entries in %ld pages\n",
|
||||
count, pages);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -7775,9 +7824,6 @@ void __init ftrace_init(void)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
pr_info("ftrace: allocating %ld entries in %ld pages\n",
|
||||
count, DIV_ROUND_UP(count, ENTRIES_PER_PAGE));
|
||||
|
||||
ret = ftrace_process_locs(NULL,
|
||||
__start_mcount_loc,
|
||||
__stop_mcount_loc);
|
||||
|
||||
@ -360,6 +360,14 @@ static int raw_notifier(struct notifier_block *nb, unsigned long msg,
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void raw_sock_destruct(struct sock *sk)
|
||||
{
|
||||
struct raw_sock *ro = raw_sk(sk);
|
||||
|
||||
free_percpu(ro->uniq);
|
||||
can_sock_destruct(sk);
|
||||
}
|
||||
|
||||
static int raw_init(struct sock *sk)
|
||||
{
|
||||
struct raw_sock *ro = raw_sk(sk);
|
||||
@ -386,6 +394,8 @@ static int raw_init(struct sock *sk)
|
||||
if (unlikely(!ro->uniq))
|
||||
return -ENOMEM;
|
||||
|
||||
sk->sk_destruct = raw_sock_destruct;
|
||||
|
||||
/* set notifier */
|
||||
spin_lock(&raw_notifier_lock);
|
||||
list_add_tail(&ro->notifier, &raw_notifier_list);
|
||||
@ -433,7 +443,6 @@ static int raw_release(struct socket *sock)
|
||||
ro->bound = 0;
|
||||
ro->dev = NULL;
|
||||
ro->count = 0;
|
||||
free_percpu(ro->uniq);
|
||||
|
||||
sock_orphan(sk);
|
||||
sock->sk = NULL;
|
||||
|
||||
@ -486,6 +486,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
|
||||
struct net *net = dev_net(skb->dev);
|
||||
struct inet6_dev *idev;
|
||||
struct ipv6hdr *oldhdr;
|
||||
unsigned int chdr_len;
|
||||
unsigned char *buf;
|
||||
int accept_rpl_seg;
|
||||
int i, err;
|
||||
@ -589,8 +590,10 @@ looped_back:
|
||||
skb_pull(skb, ((hdr->hdrlen + 1) << 3));
|
||||
skb_postpull_rcsum(skb, oldhdr,
|
||||
sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3));
|
||||
if (unlikely(!hdr->segments_left)) {
|
||||
if (pskb_expand_head(skb, sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3), 0,
|
||||
chdr_len = sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3);
|
||||
if (unlikely(!hdr->segments_left ||
|
||||
skb_headroom(skb) < chdr_len + skb->mac_len)) {
|
||||
if (pskb_expand_head(skb, chdr_len + skb->mac_len, 0,
|
||||
GFP_ATOMIC)) {
|
||||
__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTDISCARDS);
|
||||
kfree_skb(skb);
|
||||
@ -600,7 +603,7 @@ looped_back:
|
||||
|
||||
oldhdr = ipv6_hdr(skb);
|
||||
}
|
||||
skb_push(skb, ((chdr->hdrlen + 1) << 3) + sizeof(struct ipv6hdr));
|
||||
skb_push(skb, chdr_len);
|
||||
skb_reset_network_header(skb);
|
||||
skb_mac_header_rebuild(skb);
|
||||
skb_set_transport_header(skb, sizeof(struct ipv6hdr));
|
||||
|
||||
@ -602,11 +602,16 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
||||
if (!skb2)
|
||||
return 0;
|
||||
|
||||
/* Remove debris left by IPv6 stack. */
|
||||
memset(IPCB(skb2), 0, sizeof(*IPCB(skb2)));
|
||||
|
||||
skb_dst_drop(skb2);
|
||||
|
||||
skb_pull(skb2, offset);
|
||||
skb_reset_network_header(skb2);
|
||||
eiph = ip_hdr(skb2);
|
||||
if (eiph->version != 4 || eiph->ihl < 5)
|
||||
goto out;
|
||||
|
||||
/* Try to guess incoming interface */
|
||||
rt = ip_route_output_ports(dev_net(skb->dev), &fl4, NULL, eiph->saddr,
|
||||
|
||||
@ -174,12 +174,14 @@ mksysmap()
|
||||
|
||||
sorttable()
|
||||
{
|
||||
${objtree}/scripts/sorttable ${1}
|
||||
${NM} -S ${1} > .tmp_vmlinux.nm-sort
|
||||
${objtree}/scripts/sorttable -s .tmp_vmlinux.nm-sort ${1}
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -f .btf.*
|
||||
rm -f .tmp_vmlinux.nm-sort
|
||||
rm -f System.map
|
||||
rm -f vmlinux
|
||||
rm -f vmlinux.map
|
||||
|
||||
1119
scripts/sorttable.c
1119
scripts/sorttable.c
File diff suppressed because it is too large
Load Diff
@ -1,497 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* sorttable.h
|
||||
*
|
||||
* Added ORC unwind tables sort support and other updates:
|
||||
* Copyright (C) 1999-2019 Alibaba Group Holding Limited. by:
|
||||
* Shile Zhang <shile.zhang@linux.alibaba.com>
|
||||
*
|
||||
* Copyright 2011 - 2012 Cavium, Inc.
|
||||
*
|
||||
* Some of code was taken out of arch/x86/kernel/unwind_orc.c, written by:
|
||||
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
||||
*
|
||||
* Some of this code was taken out of recordmcount.h written by:
|
||||
*
|
||||
* Copyright 2009 John F. Reiser <jreiser@BitWagon.com>. All rights reserved.
|
||||
* Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
|
||||
*/
|
||||
|
||||
#undef extable_ent_size
|
||||
#undef compare_extable
|
||||
#undef get_mcount_loc
|
||||
#undef sort_mcount_loc
|
||||
#undef elf_mcount_loc
|
||||
#undef do_sort
|
||||
#undef Elf_Addr
|
||||
#undef Elf_Ehdr
|
||||
#undef Elf_Shdr
|
||||
#undef Elf_Rel
|
||||
#undef Elf_Rela
|
||||
#undef Elf_Sym
|
||||
#undef ELF_R_SYM
|
||||
#undef Elf_r_sym
|
||||
#undef ELF_R_INFO
|
||||
#undef Elf_r_info
|
||||
#undef ELF_ST_BIND
|
||||
#undef ELF_ST_TYPE
|
||||
#undef fn_ELF_R_SYM
|
||||
#undef fn_ELF_R_INFO
|
||||
#undef uint_t
|
||||
#undef _r
|
||||
#undef _w
|
||||
|
||||
#ifdef SORTTABLE_64
|
||||
# define extable_ent_size 16
|
||||
# define compare_extable compare_extable_64
|
||||
# define get_mcount_loc get_mcount_loc_64
|
||||
# define sort_mcount_loc sort_mcount_loc_64
|
||||
# define elf_mcount_loc elf_mcount_loc_64
|
||||
# define do_sort do_sort_64
|
||||
# define Elf_Addr Elf64_Addr
|
||||
# define Elf_Ehdr Elf64_Ehdr
|
||||
# define Elf_Shdr Elf64_Shdr
|
||||
# define Elf_Rel Elf64_Rel
|
||||
# define Elf_Rela Elf64_Rela
|
||||
# define Elf_Sym Elf64_Sym
|
||||
# define ELF_R_SYM ELF64_R_SYM
|
||||
# define Elf_r_sym Elf64_r_sym
|
||||
# define ELF_R_INFO ELF64_R_INFO
|
||||
# define Elf_r_info Elf64_r_info
|
||||
# define ELF_ST_BIND ELF64_ST_BIND
|
||||
# define ELF_ST_TYPE ELF64_ST_TYPE
|
||||
# define fn_ELF_R_SYM fn_ELF64_R_SYM
|
||||
# define fn_ELF_R_INFO fn_ELF64_R_INFO
|
||||
# define uint_t uint64_t
|
||||
# define _r r8
|
||||
# define _w w8
|
||||
#else
|
||||
# define extable_ent_size 8
|
||||
# define compare_extable compare_extable_32
|
||||
# define get_mcount_loc get_mcount_loc_32
|
||||
# define sort_mcount_loc sort_mcount_loc_32
|
||||
# define elf_mcount_loc elf_mcount_loc_32
|
||||
# define do_sort do_sort_32
|
||||
# define Elf_Addr Elf32_Addr
|
||||
# define Elf_Ehdr Elf32_Ehdr
|
||||
# define Elf_Shdr Elf32_Shdr
|
||||
# define Elf_Rel Elf32_Rel
|
||||
# define Elf_Rela Elf32_Rela
|
||||
# define Elf_Sym Elf32_Sym
|
||||
# define ELF_R_SYM ELF32_R_SYM
|
||||
# define Elf_r_sym Elf32_r_sym
|
||||
# define ELF_R_INFO ELF32_R_INFO
|
||||
# define Elf_r_info Elf32_r_info
|
||||
# define ELF_ST_BIND ELF32_ST_BIND
|
||||
# define ELF_ST_TYPE ELF32_ST_TYPE
|
||||
# define fn_ELF_R_SYM fn_ELF32_R_SYM
|
||||
# define fn_ELF_R_INFO fn_ELF32_R_INFO
|
||||
# define uint_t uint32_t
|
||||
# define _r r
|
||||
# define _w w
|
||||
#endif
|
||||
|
||||
#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
|
||||
/* ORC unwinder only support X86_64 */
|
||||
#include <asm/orc_types.h>
|
||||
|
||||
#define ERRSTR_MAXSZ 256
|
||||
|
||||
char g_err[ERRSTR_MAXSZ];
|
||||
int *g_orc_ip_table;
|
||||
struct orc_entry *g_orc_table;
|
||||
|
||||
pthread_t orc_sort_thread;
|
||||
|
||||
static inline unsigned long orc_ip(const int *ip)
|
||||
{
|
||||
return (unsigned long)ip + *ip;
|
||||
}
|
||||
|
||||
static int orc_sort_cmp(const void *_a, const void *_b)
|
||||
{
|
||||
struct orc_entry *orc_a;
|
||||
const int *a = g_orc_ip_table + *(int *)_a;
|
||||
const int *b = g_orc_ip_table + *(int *)_b;
|
||||
unsigned long a_val = orc_ip(a);
|
||||
unsigned long b_val = orc_ip(b);
|
||||
|
||||
if (a_val > b_val)
|
||||
return 1;
|
||||
if (a_val < b_val)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* The "weak" section terminator entries need to always be on the left
|
||||
* to ensure the lookup code skips them in favor of real entries.
|
||||
* These terminator entries exist to handle any gaps created by
|
||||
* whitelisted .o files which didn't get objtool generation.
|
||||
*/
|
||||
orc_a = g_orc_table + (a - g_orc_ip_table);
|
||||
return orc_a->type == ORC_TYPE_UNDEFINED ? -1 : 1;
|
||||
}
|
||||
|
||||
static void *sort_orctable(void *arg)
|
||||
{
|
||||
int i;
|
||||
int *idxs = NULL;
|
||||
int *tmp_orc_ip_table = NULL;
|
||||
struct orc_entry *tmp_orc_table = NULL;
|
||||
unsigned int *orc_ip_size = (unsigned int *)arg;
|
||||
unsigned int num_entries = *orc_ip_size / sizeof(int);
|
||||
unsigned int orc_size = num_entries * sizeof(struct orc_entry);
|
||||
|
||||
idxs = (int *)malloc(*orc_ip_size);
|
||||
if (!idxs) {
|
||||
snprintf(g_err, ERRSTR_MAXSZ, "malloc idxs: %s",
|
||||
strerror(errno));
|
||||
pthread_exit(g_err);
|
||||
}
|
||||
|
||||
tmp_orc_ip_table = (int *)malloc(*orc_ip_size);
|
||||
if (!tmp_orc_ip_table) {
|
||||
snprintf(g_err, ERRSTR_MAXSZ, "malloc tmp_orc_ip_table: %s",
|
||||
strerror(errno));
|
||||
pthread_exit(g_err);
|
||||
}
|
||||
|
||||
tmp_orc_table = (struct orc_entry *)malloc(orc_size);
|
||||
if (!tmp_orc_table) {
|
||||
snprintf(g_err, ERRSTR_MAXSZ, "malloc tmp_orc_table: %s",
|
||||
strerror(errno));
|
||||
pthread_exit(g_err);
|
||||
}
|
||||
|
||||
/* initialize indices array, convert ip_table to absolute address */
|
||||
for (i = 0; i < num_entries; i++) {
|
||||
idxs[i] = i;
|
||||
tmp_orc_ip_table[i] = g_orc_ip_table[i] + i * sizeof(int);
|
||||
}
|
||||
memcpy(tmp_orc_table, g_orc_table, orc_size);
|
||||
|
||||
qsort(idxs, num_entries, sizeof(int), orc_sort_cmp);
|
||||
|
||||
for (i = 0; i < num_entries; i++) {
|
||||
if (idxs[i] == i)
|
||||
continue;
|
||||
|
||||
/* convert back to relative address */
|
||||
g_orc_ip_table[i] = tmp_orc_ip_table[idxs[i]] - i * sizeof(int);
|
||||
g_orc_table[i] = tmp_orc_table[idxs[i]];
|
||||
}
|
||||
|
||||
free(idxs);
|
||||
free(tmp_orc_ip_table);
|
||||
free(tmp_orc_table);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int compare_extable(const void *a, const void *b)
|
||||
{
|
||||
Elf_Addr av = _r(a);
|
||||
Elf_Addr bv = _r(b);
|
||||
|
||||
if (av < bv)
|
||||
return -1;
|
||||
if (av > bv)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
#ifdef MCOUNT_SORT_ENABLED
|
||||
pthread_t mcount_sort_thread;
|
||||
|
||||
struct elf_mcount_loc {
|
||||
Elf_Ehdr *ehdr;
|
||||
Elf_Shdr *init_data_sec;
|
||||
uint_t start_mcount_loc;
|
||||
uint_t stop_mcount_loc;
|
||||
};
|
||||
|
||||
/* Sort the addresses stored between __start_mcount_loc to __stop_mcount_loc in vmlinux */
|
||||
static void *sort_mcount_loc(void *arg)
|
||||
{
|
||||
struct elf_mcount_loc *emloc = (struct elf_mcount_loc *)arg;
|
||||
uint_t offset = emloc->start_mcount_loc - _r(&(emloc->init_data_sec)->sh_addr)
|
||||
+ _r(&(emloc->init_data_sec)->sh_offset);
|
||||
uint_t count = emloc->stop_mcount_loc - emloc->start_mcount_loc;
|
||||
unsigned char *start_loc = (void *)emloc->ehdr + offset;
|
||||
|
||||
qsort(start_loc, count/sizeof(uint_t), sizeof(uint_t), compare_extable);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get the address of __start_mcount_loc and __stop_mcount_loc in System.map */
|
||||
static void get_mcount_loc(uint_t *_start, uint_t *_stop)
|
||||
{
|
||||
FILE *file_start, *file_stop;
|
||||
char start_buff[20];
|
||||
char stop_buff[20];
|
||||
int len = 0;
|
||||
|
||||
file_start = popen(" grep start_mcount System.map | awk '{print $1}' ", "r");
|
||||
if (!file_start) {
|
||||
fprintf(stderr, "get start_mcount_loc error!");
|
||||
return;
|
||||
}
|
||||
|
||||
file_stop = popen(" grep stop_mcount System.map | awk '{print $1}' ", "r");
|
||||
if (!file_stop) {
|
||||
fprintf(stderr, "get stop_mcount_loc error!");
|
||||
pclose(file_start);
|
||||
return;
|
||||
}
|
||||
|
||||
while (fgets(start_buff, sizeof(start_buff), file_start) != NULL) {
|
||||
len = strlen(start_buff);
|
||||
start_buff[len - 1] = '\0';
|
||||
}
|
||||
*_start = strtoul(start_buff, NULL, 16);
|
||||
|
||||
while (fgets(stop_buff, sizeof(stop_buff), file_stop) != NULL) {
|
||||
len = strlen(stop_buff);
|
||||
stop_buff[len - 1] = '\0';
|
||||
}
|
||||
*_stop = strtoul(stop_buff, NULL, 16);
|
||||
|
||||
pclose(file_start);
|
||||
pclose(file_stop);
|
||||
}
|
||||
#endif
|
||||
static int do_sort(Elf_Ehdr *ehdr,
|
||||
char const *const fname,
|
||||
table_sort_t custom_sort)
|
||||
{
|
||||
int rc = -1;
|
||||
Elf_Shdr *s, *shdr = (Elf_Shdr *)((char *)ehdr + _r(&ehdr->e_shoff));
|
||||
Elf_Shdr *strtab_sec = NULL;
|
||||
Elf_Shdr *symtab_sec = NULL;
|
||||
Elf_Shdr *extab_sec = NULL;
|
||||
Elf_Sym *sym;
|
||||
const Elf_Sym *symtab;
|
||||
Elf32_Word *symtab_shndx = NULL;
|
||||
Elf_Sym *sort_needed_sym = NULL;
|
||||
Elf_Shdr *sort_needed_sec;
|
||||
Elf_Rel *relocs = NULL;
|
||||
int relocs_size = 0;
|
||||
uint32_t *sort_needed_loc;
|
||||
const char *secstrings;
|
||||
const char *strtab;
|
||||
char *extab_image;
|
||||
int extab_index = 0;
|
||||
int i;
|
||||
int idx;
|
||||
unsigned int shnum;
|
||||
unsigned int shstrndx;
|
||||
#ifdef MCOUNT_SORT_ENABLED
|
||||
struct elf_mcount_loc mstruct = {0};
|
||||
uint_t _start_mcount_loc = 0;
|
||||
uint_t _stop_mcount_loc = 0;
|
||||
#endif
|
||||
#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
|
||||
unsigned int orc_ip_size = 0;
|
||||
unsigned int orc_size = 0;
|
||||
unsigned int orc_num_entries = 0;
|
||||
#endif
|
||||
|
||||
shstrndx = r2(&ehdr->e_shstrndx);
|
||||
if (shstrndx == SHN_XINDEX)
|
||||
shstrndx = r(&shdr[0].sh_link);
|
||||
secstrings = (const char *)ehdr + _r(&shdr[shstrndx].sh_offset);
|
||||
|
||||
shnum = r2(&ehdr->e_shnum);
|
||||
if (shnum == SHN_UNDEF)
|
||||
shnum = _r(&shdr[0].sh_size);
|
||||
|
||||
for (i = 0, s = shdr; s < shdr + shnum; i++, s++) {
|
||||
idx = r(&s->sh_name);
|
||||
if (!strcmp(secstrings + idx, "__ex_table")) {
|
||||
extab_sec = s;
|
||||
extab_index = i;
|
||||
}
|
||||
if (!strcmp(secstrings + idx, ".symtab"))
|
||||
symtab_sec = s;
|
||||
if (!strcmp(secstrings + idx, ".strtab"))
|
||||
strtab_sec = s;
|
||||
|
||||
if ((r(&s->sh_type) == SHT_REL ||
|
||||
r(&s->sh_type) == SHT_RELA) &&
|
||||
r(&s->sh_info) == extab_index) {
|
||||
relocs = (void *)ehdr + _r(&s->sh_offset);
|
||||
relocs_size = _r(&s->sh_size);
|
||||
}
|
||||
if (r(&s->sh_type) == SHT_SYMTAB_SHNDX)
|
||||
symtab_shndx = (Elf32_Word *)((const char *)ehdr +
|
||||
_r(&s->sh_offset));
|
||||
|
||||
#ifdef MCOUNT_SORT_ENABLED
|
||||
/* locate the .init.data section in vmlinux */
|
||||
if (!strcmp(secstrings + idx, ".init.data")) {
|
||||
get_mcount_loc(&_start_mcount_loc, &_stop_mcount_loc);
|
||||
mstruct.ehdr = ehdr;
|
||||
mstruct.init_data_sec = s;
|
||||
mstruct.start_mcount_loc = _start_mcount_loc;
|
||||
mstruct.stop_mcount_loc = _stop_mcount_loc;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
|
||||
/* locate the ORC unwind tables */
|
||||
if (!strcmp(secstrings + idx, ".orc_unwind_ip")) {
|
||||
orc_ip_size = s->sh_size;
|
||||
g_orc_ip_table = (int *)((void *)ehdr +
|
||||
s->sh_offset);
|
||||
}
|
||||
if (!strcmp(secstrings + idx, ".orc_unwind")) {
|
||||
orc_size = s->sh_size;
|
||||
g_orc_table = (struct orc_entry *)((void *)ehdr +
|
||||
s->sh_offset);
|
||||
}
|
||||
#endif
|
||||
} /* for loop */
|
||||
|
||||
#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
|
||||
if (!g_orc_ip_table || !g_orc_table) {
|
||||
fprintf(stderr,
|
||||
"incomplete ORC unwind tables in file: %s\n", fname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
orc_num_entries = orc_ip_size / sizeof(int);
|
||||
if (orc_ip_size % sizeof(int) != 0 ||
|
||||
orc_size % sizeof(struct orc_entry) != 0 ||
|
||||
orc_num_entries != orc_size / sizeof(struct orc_entry)) {
|
||||
fprintf(stderr,
|
||||
"inconsistent ORC unwind table entries in file: %s\n",
|
||||
fname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* create thread to sort ORC unwind tables concurrently */
|
||||
if (pthread_create(&orc_sort_thread, NULL,
|
||||
sort_orctable, &orc_ip_size)) {
|
||||
fprintf(stderr,
|
||||
"pthread_create orc_sort_thread failed '%s': %s\n",
|
||||
strerror(errno), fname);
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MCOUNT_SORT_ENABLED
|
||||
if (!mstruct.init_data_sec || !_start_mcount_loc || !_stop_mcount_loc) {
|
||||
fprintf(stderr,
|
||||
"incomplete mcount's sort in file: %s\n",
|
||||
fname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* create thread to sort mcount_loc concurrently */
|
||||
if (pthread_create(&mcount_sort_thread, NULL, &sort_mcount_loc, &mstruct)) {
|
||||
fprintf(stderr,
|
||||
"pthread_create mcount_sort_thread failed '%s': %s\n",
|
||||
strerror(errno), fname);
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
if (!extab_sec) {
|
||||
fprintf(stderr, "no __ex_table in file: %s\n", fname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!symtab_sec) {
|
||||
fprintf(stderr, "no .symtab in file: %s\n", fname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!strtab_sec) {
|
||||
fprintf(stderr, "no .strtab in file: %s\n", fname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
extab_image = (void *)ehdr + _r(&extab_sec->sh_offset);
|
||||
strtab = (const char *)ehdr + _r(&strtab_sec->sh_offset);
|
||||
symtab = (const Elf_Sym *)((const char *)ehdr +
|
||||
_r(&symtab_sec->sh_offset));
|
||||
|
||||
if (custom_sort) {
|
||||
custom_sort(extab_image, _r(&extab_sec->sh_size));
|
||||
} else {
|
||||
int num_entries = _r(&extab_sec->sh_size) / extable_ent_size;
|
||||
qsort(extab_image, num_entries,
|
||||
extable_ent_size, compare_extable);
|
||||
}
|
||||
|
||||
/* If there were relocations, we no longer need them. */
|
||||
if (relocs)
|
||||
memset(relocs, 0, relocs_size);
|
||||
|
||||
/* find the flag main_extable_sort_needed */
|
||||
for (sym = (void *)ehdr + _r(&symtab_sec->sh_offset);
|
||||
sym < sym + _r(&symtab_sec->sh_size) / sizeof(Elf_Sym);
|
||||
sym++) {
|
||||
if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT)
|
||||
continue;
|
||||
if (!strcmp(strtab + r(&sym->st_name),
|
||||
"main_extable_sort_needed")) {
|
||||
sort_needed_sym = sym;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sort_needed_sym) {
|
||||
fprintf(stderr,
|
||||
"no main_extable_sort_needed symbol in file: %s\n",
|
||||
fname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
sort_needed_sec = &shdr[get_secindex(r2(&sym->st_shndx),
|
||||
sort_needed_sym - symtab,
|
||||
symtab_shndx)];
|
||||
sort_needed_loc = (void *)ehdr +
|
||||
_r(&sort_needed_sec->sh_offset) +
|
||||
_r(&sort_needed_sym->st_value) -
|
||||
_r(&sort_needed_sec->sh_addr);
|
||||
|
||||
/* extable has been sorted, clear the flag */
|
||||
w(0, sort_needed_loc);
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
#if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
|
||||
if (orc_sort_thread) {
|
||||
void *retval = NULL;
|
||||
/* wait for ORC tables sort done */
|
||||
rc = pthread_join(orc_sort_thread, &retval);
|
||||
if (rc) {
|
||||
fprintf(stderr,
|
||||
"pthread_join failed '%s': %s\n",
|
||||
strerror(errno), fname);
|
||||
} else if (retval) {
|
||||
rc = -1;
|
||||
fprintf(stderr,
|
||||
"failed to sort ORC tables '%s': %s\n",
|
||||
(char *)retval, fname);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MCOUNT_SORT_ENABLED
|
||||
if (mcount_sort_thread) {
|
||||
void *retval = NULL;
|
||||
/* wait for mcount sort done */
|
||||
rc = pthread_join(mcount_sort_thread, &retval);
|
||||
if (rc) {
|
||||
fprintf(stderr,
|
||||
"pthread_join failed '%s': %s\n",
|
||||
strerror(errno), fname);
|
||||
} else if (retval) {
|
||||
rc = -1;
|
||||
fprintf(stderr,
|
||||
"failed to sort mcount '%s': %s\n",
|
||||
(char *)retval, fname);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
@ -11,7 +11,7 @@ obj-$(CONFIG_SECURITY) += lsm_syscalls.o
|
||||
obj-$(CONFIG_MMU) += min_addr.o
|
||||
|
||||
# Object file lists
|
||||
obj-$(CONFIG_SECURITY) += security.o
|
||||
obj-$(CONFIG_SECURITY) += security.o lsm_notifier.o lsm_init.o
|
||||
obj-$(CONFIG_SECURITYFS) += inode.o
|
||||
obj-$(CONFIG_SECURITY_SELINUX) += selinux/
|
||||
obj-$(CONFIG_SECURITY_SMACK) += smack/
|
||||
|
||||
@ -2267,7 +2267,7 @@ alloc_out:
|
||||
}
|
||||
|
||||
DEFINE_LSM(apparmor) = {
|
||||
.name = "apparmor",
|
||||
.id = &apparmor_lsmid,
|
||||
.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
|
||||
.enabled = &apparmor_enabled,
|
||||
.blobs = &apparmor_blob_sizes,
|
||||
|
||||
@ -33,7 +33,7 @@ struct lsm_blob_sizes bpf_lsm_blob_sizes __ro_after_init = {
|
||||
};
|
||||
|
||||
DEFINE_LSM(bpf) = {
|
||||
.name = "bpf",
|
||||
.id = &bpf_lsmid,
|
||||
.init = bpf_lsm_init,
|
||||
.blobs = &bpf_lsm_blob_sizes
|
||||
};
|
||||
|
||||
@ -1470,7 +1470,7 @@ static int __init capability_init(void)
|
||||
}
|
||||
|
||||
DEFINE_LSM(capability) = {
|
||||
.name = "capability",
|
||||
.id = &capability_lsmid,
|
||||
.order = LSM_ORDER_FIRST,
|
||||
.init = capability_init,
|
||||
};
|
||||
|
||||
@ -22,6 +22,8 @@
|
||||
#include <linux/lsm_hooks.h>
|
||||
#include <linux/magic.h>
|
||||
|
||||
#include "lsm.h"
|
||||
|
||||
static struct vfsmount *mount;
|
||||
static int mount_count;
|
||||
|
||||
@ -339,12 +341,49 @@ void securityfs_recursive_remove(struct dentry *dentry)
|
||||
EXPORT_SYMBOL_GPL(securityfs_recursive_remove);
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
static struct dentry *lsm_dentry;
|
||||
|
||||
static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
return simple_read_from_buffer(buf, count, ppos, lsm_names,
|
||||
strlen(lsm_names));
|
||||
int i;
|
||||
static char *str;
|
||||
static size_t len;
|
||||
static DEFINE_SPINLOCK(lock);
|
||||
|
||||
/* NOTE: we never free or modify the string once it is set */
|
||||
|
||||
if (unlikely(!str || !len)) {
|
||||
char *str_tmp;
|
||||
size_t len_tmp = 0;
|
||||
|
||||
for (i = 0; i < lsm_active_cnt; i++)
|
||||
/* the '+ 1' accounts for either a comma or a NUL */
|
||||
len_tmp += strlen(lsm_idlist[i]->name) + 1;
|
||||
|
||||
str_tmp = kmalloc(len_tmp, GFP_KERNEL);
|
||||
if (!str_tmp)
|
||||
return -ENOMEM;
|
||||
str_tmp[0] = '\0';
|
||||
|
||||
for (i = 0; i < lsm_active_cnt; i++) {
|
||||
if (i > 0)
|
||||
strcat(str_tmp, ",");
|
||||
strcat(str_tmp, lsm_idlist[i]->name);
|
||||
}
|
||||
|
||||
spin_lock(&lock);
|
||||
if (!str) {
|
||||
str = str_tmp;
|
||||
len = len_tmp - 1;
|
||||
} else
|
||||
kfree(str_tmp);
|
||||
spin_unlock(&lock);
|
||||
}
|
||||
|
||||
return simple_read_from_buffer(buf, count, ppos, str, len);
|
||||
}
|
||||
|
||||
static const struct file_operations lsm_ops = {
|
||||
|
||||
@ -1175,7 +1175,7 @@ struct lsm_blob_sizes evm_blob_sizes __ro_after_init = {
|
||||
};
|
||||
|
||||
DEFINE_LSM(evm) = {
|
||||
.name = "evm",
|
||||
.id = &evm_lsmid,
|
||||
.init = init_evm_lsm,
|
||||
.order = LSM_ORDER_LAST,
|
||||
.blobs = &evm_blob_sizes,
|
||||
|
||||
@ -1222,7 +1222,7 @@ struct lsm_blob_sizes ima_blob_sizes __ro_after_init = {
|
||||
};
|
||||
|
||||
DEFINE_LSM(ima) = {
|
||||
.name = "ima",
|
||||
.id = &ima_lsmid,
|
||||
.init = init_ima_lsm,
|
||||
.order = LSM_ORDER_LAST,
|
||||
.blobs = &ima_blob_sizes,
|
||||
|
||||
@ -92,7 +92,7 @@ static int __init ipe_init(void)
|
||||
}
|
||||
|
||||
DEFINE_LSM(ipe) = {
|
||||
.name = "ipe",
|
||||
.id = &ipe_lsmid,
|
||||
.init = ipe_init,
|
||||
.blobs = &ipe_blobs,
|
||||
};
|
||||
|
||||
@ -43,7 +43,7 @@ static int __init landlock_init(void)
|
||||
}
|
||||
|
||||
DEFINE_LSM(LANDLOCK_NAME) = {
|
||||
.name = LANDLOCK_NAME,
|
||||
.id = &landlock_lsmid,
|
||||
.init = landlock_init,
|
||||
.blobs = &landlock_blob_sizes,
|
||||
};
|
||||
|
||||
@ -271,7 +271,7 @@ static int __init loadpin_init(void)
|
||||
}
|
||||
|
||||
DEFINE_LSM(loadpin) = {
|
||||
.name = "loadpin",
|
||||
.id = &loadpin_lsmid,
|
||||
.init = loadpin_init,
|
||||
};
|
||||
|
||||
|
||||
@ -169,6 +169,6 @@ DEFINE_EARLY_LSM(lockdown) = {
|
||||
#else
|
||||
DEFINE_LSM(lockdown) = {
|
||||
#endif
|
||||
.name = "lockdown",
|
||||
.id = &lockdown_lsmid,
|
||||
.init = lockdown_lsm_init,
|
||||
};
|
||||
|
||||
39
security/lsm.h
Normal file
39
security/lsm.h
Normal file
@ -0,0 +1,39 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* LSM functions
|
||||
*/
|
||||
|
||||
#ifndef _LSM_H_
|
||||
#define _LSM_H_
|
||||
|
||||
#include <linux/printk.h>
|
||||
#include <linux/lsm_hooks.h>
|
||||
#include <linux/lsm_count.h>
|
||||
|
||||
/* LSM debugging */
|
||||
extern bool lsm_debug;
|
||||
#define lsm_pr(...) pr_info(__VA_ARGS__)
|
||||
#define lsm_pr_cont(...) pr_cont(__VA_ARGS__)
|
||||
#define lsm_pr_dbg(...) \
|
||||
do { \
|
||||
if (lsm_debug) \
|
||||
pr_info(__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
/* List of configured LSMs */
|
||||
extern unsigned int lsm_active_cnt;
|
||||
extern const struct lsm_id *lsm_idlist[];
|
||||
|
||||
/* LSM blob configuration */
|
||||
extern struct lsm_blob_sizes blob_sizes;
|
||||
|
||||
/* LSM blob caches */
|
||||
extern struct kmem_cache *lsm_file_cache;
|
||||
extern struct kmem_cache *lsm_backing_file_cache;
|
||||
extern struct kmem_cache *lsm_inode_cache;
|
||||
|
||||
/* LSM blob allocators */
|
||||
int lsm_cred_alloc(struct cred *cred, gfp_t gfp);
|
||||
int lsm_task_alloc(struct task_struct *task);
|
||||
|
||||
#endif /* _LSM_H_ */
|
||||
454
security/lsm_init.c
Normal file
454
security/lsm_init.c
Normal file
@ -0,0 +1,454 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* LSM initialization functions
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "LSM: " fmt
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/lsm_hooks.h>
|
||||
|
||||
#include "lsm.h"
|
||||
|
||||
/* LSM enabled constants. */
|
||||
static __initdata int lsm_enabled_true = 1;
|
||||
static __initdata int lsm_enabled_false = 0;
|
||||
|
||||
/* Pointers to LSM sections defined in include/asm-generic/vmlinux.lds.h */
|
||||
extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
|
||||
extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
|
||||
|
||||
/* Number of "early" LSMs */
|
||||
static __initdata unsigned int lsm_count_early;
|
||||
|
||||
/* Build and boot-time LSM ordering. */
|
||||
static __initconst const char *const lsm_order_builtin = CONFIG_LSM;
|
||||
static __initdata const char *lsm_order_cmdline;
|
||||
static __initdata const char *lsm_order_legacy;
|
||||
|
||||
/* Ordered list of LSMs to initialize. */
|
||||
static __initdata struct lsm_info *lsm_order[MAX_LSM_COUNT + 1];
|
||||
static __initdata struct lsm_info *lsm_exclusive;
|
||||
|
||||
#define lsm_order_for_each(iter) \
|
||||
for ((iter) = lsm_order; *(iter); (iter)++)
|
||||
#define lsm_for_each_raw(iter) \
|
||||
for ((iter) = __start_lsm_info; \
|
||||
(iter) < __end_lsm_info; (iter)++)
|
||||
#define lsm_early_for_each_raw(iter) \
|
||||
for ((iter) = __start_early_lsm_info; \
|
||||
(iter) < __end_early_lsm_info; (iter)++)
|
||||
|
||||
/**
|
||||
* lsm_choose_security - Legacy "major" LSM selection
|
||||
* @str: kernel command line parameter
|
||||
*/
|
||||
static int __init lsm_choose_security(char *str)
|
||||
{
|
||||
lsm_order_legacy = str;
|
||||
return 1;
|
||||
}
|
||||
__setup("security=", lsm_choose_security);
|
||||
|
||||
/**
|
||||
* lsm_choose_lsm - Modern LSM selection
|
||||
* @str: kernel command line parameter
|
||||
*/
|
||||
static int __init lsm_choose_lsm(char *str)
|
||||
{
|
||||
lsm_order_cmdline = str;
|
||||
return 1;
|
||||
}
|
||||
__setup("lsm=", lsm_choose_lsm);
|
||||
|
||||
/**
|
||||
* lsm_debug_enable - Enable LSM framework debugging
|
||||
* @str: kernel command line parameter
|
||||
*
|
||||
* Currently we only provide debug info during LSM initialization, but we may
|
||||
* want to expand this in the future.
|
||||
*/
|
||||
static int __init lsm_debug_enable(char *str)
|
||||
{
|
||||
lsm_debug = true;
|
||||
return 1;
|
||||
}
|
||||
__setup("lsm.debug", lsm_debug_enable);
|
||||
|
||||
/**
|
||||
* lsm_enabled_set - Mark a LSM as enabled
|
||||
* @lsm: LSM definition
|
||||
* @enabled: enabled flag
|
||||
*/
|
||||
static void __init lsm_enabled_set(struct lsm_info *lsm, bool enabled)
|
||||
{
|
||||
/*
|
||||
* When an LSM hasn't configured an enable variable, we can use
|
||||
* a hard-coded location for storing the default enabled state.
|
||||
*/
|
||||
if (!lsm->enabled ||
|
||||
lsm->enabled == &lsm_enabled_true ||
|
||||
lsm->enabled == &lsm_enabled_false) {
|
||||
lsm->enabled = enabled ? &lsm_enabled_true : &lsm_enabled_false;
|
||||
} else {
|
||||
*lsm->enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_is_enabled - Determine if a LSM is enabled
|
||||
* @lsm: LSM definition
|
||||
*/
|
||||
static inline bool lsm_is_enabled(struct lsm_info *lsm)
|
||||
{
|
||||
return (lsm->enabled ? *lsm->enabled : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_order_exists - Determine if a LSM exists in the ordered list
|
||||
* @lsm: LSM definition
|
||||
*/
|
||||
static bool __init lsm_order_exists(struct lsm_info *lsm)
|
||||
{
|
||||
struct lsm_info **check;
|
||||
|
||||
lsm_order_for_each(check) {
|
||||
if (*check == lsm)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_order_append - Append a LSM to the ordered list
|
||||
* @lsm: LSM definition
|
||||
* @src: source of the addition
|
||||
*
|
||||
* Append @lsm to the enabled LSM array after ensuring that it hasn't been
|
||||
* explicitly disabled, is a duplicate entry, or would run afoul of the
|
||||
* LSM_FLAG_EXCLUSIVE logic.
|
||||
*/
|
||||
static void __init lsm_order_append(struct lsm_info *lsm, const char *src)
|
||||
{
|
||||
/* Ignore duplicate selections. */
|
||||
if (lsm_order_exists(lsm))
|
||||
return;
|
||||
|
||||
/* Skip explicitly disabled LSMs. */
|
||||
if (lsm->enabled && !lsm_is_enabled(lsm)) {
|
||||
lsm_pr_dbg("skip previously disabled LSM %s:%s\n",
|
||||
src, lsm->id->name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lsm_active_cnt == MAX_LSM_COUNT) {
|
||||
pr_warn("exceeded maximum LSM count on %s:%s\n",
|
||||
src, lsm->id->name);
|
||||
lsm_enabled_set(lsm, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lsm->flags & LSM_FLAG_EXCLUSIVE) {
|
||||
if (lsm_exclusive) {
|
||||
lsm_pr_dbg("skip exclusive LSM conflict %s:%s\n",
|
||||
src, lsm->id->name);
|
||||
lsm_enabled_set(lsm, false);
|
||||
return;
|
||||
} else {
|
||||
lsm_pr_dbg("select exclusive LSM %s:%s\n",
|
||||
src, lsm->id->name);
|
||||
lsm_exclusive = lsm;
|
||||
}
|
||||
}
|
||||
|
||||
lsm_enabled_set(lsm, true);
|
||||
lsm_order[lsm_active_cnt] = lsm;
|
||||
lsm_idlist[lsm_active_cnt++] = lsm->id;
|
||||
|
||||
lsm_pr_dbg("enabling LSM %s:%s\n", src, lsm->id->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_blob_size_update - Update the LSM blob size and offset information
|
||||
* @sz_req: the requested additional blob size
|
||||
* @sz_cur: the existing blob size
|
||||
*/
|
||||
static void __init lsm_blob_size_update(unsigned int *sz_req,
|
||||
unsigned int *sz_cur)
|
||||
{
|
||||
unsigned int offset;
|
||||
|
||||
if (*sz_req == 0)
|
||||
return;
|
||||
|
||||
offset = ALIGN(*sz_cur, sizeof(void *));
|
||||
*sz_cur = offset + *sz_req;
|
||||
*sz_req = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_prepare - Prepare the LSM framework for a new LSM
|
||||
* @lsm: LSM definition
|
||||
*/
|
||||
static void __init lsm_prepare(struct lsm_info *lsm)
|
||||
{
|
||||
struct lsm_blob_sizes *blobs = lsm->blobs;
|
||||
|
||||
if (!blobs)
|
||||
return;
|
||||
|
||||
/* Register the LSM blob sizes. */
|
||||
blobs = lsm->blobs;
|
||||
lsm_blob_size_update(&blobs->lbs_cred, &blob_sizes.lbs_cred);
|
||||
lsm_blob_size_update(&blobs->lbs_file, &blob_sizes.lbs_file);
|
||||
lsm_blob_size_update(&blobs->lbs_backing_file,
|
||||
&blob_sizes.lbs_backing_file);
|
||||
lsm_blob_size_update(&blobs->lbs_ib, &blob_sizes.lbs_ib);
|
||||
/* inode blob gets an rcu_head in addition to LSM blobs. */
|
||||
if (blobs->lbs_inode && blob_sizes.lbs_inode == 0)
|
||||
blob_sizes.lbs_inode = sizeof(struct rcu_head);
|
||||
lsm_blob_size_update(&blobs->lbs_inode, &blob_sizes.lbs_inode);
|
||||
lsm_blob_size_update(&blobs->lbs_ipc, &blob_sizes.lbs_ipc);
|
||||
lsm_blob_size_update(&blobs->lbs_key, &blob_sizes.lbs_key);
|
||||
lsm_blob_size_update(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
|
||||
lsm_blob_size_update(&blobs->lbs_perf_event,
|
||||
&blob_sizes.lbs_perf_event);
|
||||
lsm_blob_size_update(&blobs->lbs_sock, &blob_sizes.lbs_sock);
|
||||
lsm_blob_size_update(&blobs->lbs_superblock,
|
||||
&blob_sizes.lbs_superblock);
|
||||
lsm_blob_size_update(&blobs->lbs_task, &blob_sizes.lbs_task);
|
||||
lsm_blob_size_update(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
|
||||
lsm_blob_size_update(&blobs->lbs_xattr_count,
|
||||
&blob_sizes.lbs_xattr_count);
|
||||
lsm_blob_size_update(&blobs->lbs_bdev, &blob_sizes.lbs_bdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_init_single - Initialize a given LSM
|
||||
* @lsm: LSM definition
|
||||
*/
|
||||
static void __init lsm_init_single(struct lsm_info *lsm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!lsm_is_enabled(lsm))
|
||||
return;
|
||||
|
||||
lsm_pr_dbg("initializing %s\n", lsm->id->name);
|
||||
ret = lsm->init();
|
||||
WARN(ret, "%s failed to initialize: %d\n", lsm->id->name, ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_order_parse - Parse the comma delimited LSM list
|
||||
* @list: LSM list
|
||||
* @src: source of the list
|
||||
*/
|
||||
static void __init lsm_order_parse(const char *list, const char *src)
|
||||
{
|
||||
struct lsm_info *lsm;
|
||||
char *sep, *name, *next;
|
||||
|
||||
/* Handle any Legacy LSM exclusions if one was specified. */
|
||||
if (lsm_order_legacy) {
|
||||
/*
|
||||
* To match the original "security=" behavior, this explicitly
|
||||
* does NOT fallback to another Legacy Major if the selected
|
||||
* one was separately disabled: disable all non-matching
|
||||
* Legacy Major LSMs.
|
||||
*/
|
||||
lsm_for_each_raw(lsm) {
|
||||
if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) &&
|
||||
strcmp(lsm->id->name, lsm_order_legacy)) {
|
||||
lsm_enabled_set(lsm, false);
|
||||
lsm_pr_dbg("skip legacy LSM conflict %s:%s\n",
|
||||
src, lsm->id->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* LSM_ORDER_FIRST */
|
||||
lsm_for_each_raw(lsm) {
|
||||
if (lsm->order == LSM_ORDER_FIRST)
|
||||
lsm_order_append(lsm, "first");
|
||||
}
|
||||
|
||||
/* Normal or "mutable" LSMs */
|
||||
sep = kstrdup(list, GFP_KERNEL);
|
||||
next = sep;
|
||||
/* Walk the list, looking for matching LSMs. */
|
||||
while ((name = strsep(&next, ",")) != NULL) {
|
||||
lsm_for_each_raw(lsm) {
|
||||
if (!strcmp(lsm->id->name, name) &&
|
||||
lsm->order == LSM_ORDER_MUTABLE)
|
||||
lsm_order_append(lsm, src);
|
||||
}
|
||||
}
|
||||
kfree(sep);
|
||||
|
||||
/* Legacy LSM if specified. */
|
||||
if (lsm_order_legacy) {
|
||||
lsm_for_each_raw(lsm) {
|
||||
if (!strcmp(lsm->id->name, lsm_order_legacy))
|
||||
lsm_order_append(lsm, src);
|
||||
}
|
||||
}
|
||||
|
||||
/* LSM_ORDER_LAST */
|
||||
lsm_for_each_raw(lsm) {
|
||||
if (lsm->order == LSM_ORDER_LAST)
|
||||
lsm_order_append(lsm, "last");
|
||||
}
|
||||
|
||||
/* Disable all LSMs not previously enabled. */
|
||||
lsm_for_each_raw(lsm) {
|
||||
if (lsm_order_exists(lsm))
|
||||
continue;
|
||||
lsm_enabled_set(lsm, false);
|
||||
lsm_pr_dbg("skip disabled LSM %s:%s\n", src, lsm->id->name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_static_call_init - Initialize a LSM's static calls
|
||||
* @hl: LSM hook list
|
||||
*/
|
||||
static int __init lsm_static_call_init(struct security_hook_list *hl)
|
||||
{
|
||||
struct lsm_static_call *scall = hl->scalls;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_LSM_COUNT; i++) {
|
||||
/* Update the first static call that is not used yet */
|
||||
if (!scall->hl) {
|
||||
__static_call_update(scall->key, scall->trampoline,
|
||||
hl->hook.lsm_func_addr);
|
||||
scall->hl = hl;
|
||||
static_branch_enable(scall->active);
|
||||
return 0;
|
||||
}
|
||||
scall++;
|
||||
}
|
||||
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/**
|
||||
* security_add_hooks - Add a LSM's hooks to the LSM framework's hook lists
|
||||
* @hooks: LSM hooks to add
|
||||
* @count: number of hooks to add
|
||||
* @lsmid: identification information for the LSM
|
||||
*
|
||||
* Each LSM has to register its hooks with the LSM framework.
|
||||
*/
|
||||
void __init security_add_hooks(struct security_hook_list *hooks, int count,
|
||||
const struct lsm_id *lsmid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
hooks[i].lsmid = lsmid;
|
||||
if (lsm_static_call_init(&hooks[i]))
|
||||
panic("exhausted LSM callback slots with LSM %s\n",
|
||||
lsmid->name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* early_security_init - Initialize the early LSMs
|
||||
*/
|
||||
int __init early_security_init(void)
|
||||
{
|
||||
struct lsm_info *lsm;
|
||||
static bool early_security_initialized;
|
||||
|
||||
if (early_security_initialized)
|
||||
return 0;
|
||||
|
||||
lsm_early_for_each_raw(lsm) {
|
||||
lsm_enabled_set(lsm, true);
|
||||
lsm_order_append(lsm, "early");
|
||||
lsm_prepare(lsm);
|
||||
lsm_init_single(lsm);
|
||||
lsm_count_early++;
|
||||
}
|
||||
|
||||
early_security_initialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* security_init - Initializes the LSM framework
|
||||
*
|
||||
* This should be called early in the kernel initialization sequence.
|
||||
*/
|
||||
int __init security_init(void)
|
||||
{
|
||||
unsigned int cnt;
|
||||
struct lsm_info **lsm;
|
||||
|
||||
if (lsm_debug) {
|
||||
lsm_pr("built-in LSM list: %s\n", lsm_order_builtin);
|
||||
lsm_pr("legacy LSM parameter: %s\n", lsm_order_legacy);
|
||||
lsm_pr("boot LSM parameter: %s\n", lsm_order_cmdline);
|
||||
}
|
||||
|
||||
if (lsm_order_cmdline) {
|
||||
if (lsm_order_legacy)
|
||||
lsm_order_legacy = NULL;
|
||||
lsm_order_parse(lsm_order_cmdline, "cmdline");
|
||||
} else
|
||||
lsm_order_parse(lsm_order_builtin, "builtin");
|
||||
|
||||
lsm_order_for_each(lsm)
|
||||
lsm_prepare(*lsm);
|
||||
|
||||
if (lsm_debug) {
|
||||
lsm_pr("blob(cred) size %d\n", blob_sizes.lbs_cred);
|
||||
lsm_pr("blob(file) size %d\n", blob_sizes.lbs_file);
|
||||
lsm_pr("blob(backing_file) size %d\n",
|
||||
blob_sizes.lbs_backing_file);
|
||||
lsm_pr("blob(ib) size %d\n", blob_sizes.lbs_ib);
|
||||
lsm_pr("blob(inode) size %d\n", blob_sizes.lbs_inode);
|
||||
lsm_pr("blob(ipc) size %d\n", blob_sizes.lbs_ipc);
|
||||
lsm_pr("blob(key) size %d\n", blob_sizes.lbs_key);
|
||||
lsm_pr("blob(msg_msg)_size %d\n", blob_sizes.lbs_msg_msg);
|
||||
lsm_pr("blob(sock) size %d\n", blob_sizes.lbs_sock);
|
||||
lsm_pr("blob(superblock) size %d\n", blob_sizes.lbs_superblock);
|
||||
lsm_pr("blob(perf_event) size %d\n", blob_sizes.lbs_perf_event);
|
||||
lsm_pr("blob(task) size %d\n", blob_sizes.lbs_task);
|
||||
lsm_pr("blob(tun_dev) size %d\n", blob_sizes.lbs_tun_dev);
|
||||
lsm_pr("blob(xattr) count %d\n", blob_sizes.lbs_xattr_count);
|
||||
lsm_pr("blob(bdev) size %d\n", blob_sizes.lbs_bdev);
|
||||
}
|
||||
|
||||
if (blob_sizes.lbs_file)
|
||||
lsm_file_cache = kmem_cache_create("lsm_file_cache",
|
||||
blob_sizes.lbs_file, 0,
|
||||
SLAB_PANIC, NULL);
|
||||
if (blob_sizes.lbs_backing_file)
|
||||
lsm_backing_file_cache = kmem_cache_create(
|
||||
"lsm_backing_file_cache",
|
||||
blob_sizes.lbs_backing_file,
|
||||
0, SLAB_PANIC, NULL);
|
||||
if (blob_sizes.lbs_inode)
|
||||
lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
|
||||
blob_sizes.lbs_inode, 0,
|
||||
SLAB_PANIC, NULL);
|
||||
|
||||
if (lsm_cred_alloc((struct cred __rcu *)current->cred, GFP_KERNEL))
|
||||
panic("early LSM cred alloc failed\n");
|
||||
if (lsm_task_alloc(current))
|
||||
panic("early LSM task alloc failed\n");
|
||||
|
||||
cnt = 0;
|
||||
lsm_order_for_each(lsm) {
|
||||
/* skip the "early" LSMs as they have already been setup */
|
||||
if (cnt++ < lsm_count_early)
|
||||
continue;
|
||||
lsm_init_single(*lsm);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
31
security/lsm_notifier.c
Normal file
31
security/lsm_notifier.c
Normal file
@ -0,0 +1,31 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* LSM notifier functions
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/security.h>
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
|
||||
|
||||
int call_blocking_lsm_notifier(enum lsm_event event, void *data)
|
||||
{
|
||||
return blocking_notifier_call_chain(&blocking_lsm_notifier_chain,
|
||||
event, data);
|
||||
}
|
||||
EXPORT_SYMBOL(call_blocking_lsm_notifier);
|
||||
|
||||
int register_blocking_lsm_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(&blocking_lsm_notifier_chain,
|
||||
nb);
|
||||
}
|
||||
EXPORT_SYMBOL(register_blocking_lsm_notifier);
|
||||
|
||||
int unregister_blocking_lsm_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain,
|
||||
nb);
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
|
||||
@ -17,6 +17,8 @@
|
||||
#include <linux/lsm_hooks.h>
|
||||
#include <uapi/linux/lsm.h>
|
||||
|
||||
#include "lsm.h"
|
||||
|
||||
/**
|
||||
* lsm_name_to_attr - map an LSM attribute name to its ID
|
||||
* @name: name of the attribute
|
||||
|
||||
@ -287,6 +287,6 @@ static int __init safesetid_security_init(void)
|
||||
}
|
||||
|
||||
DEFINE_LSM(safesetid_security_init) = {
|
||||
.id = &safesetid_lsmid,
|
||||
.init = safesetid_security_init,
|
||||
.name = "safesetid",
|
||||
};
|
||||
|
||||
@ -33,24 +33,7 @@
|
||||
#include <net/flow.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX
|
||||
|
||||
/*
|
||||
* Identifier for the LSM static calls.
|
||||
* HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
|
||||
* IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
|
||||
*/
|
||||
#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX
|
||||
|
||||
/*
|
||||
* Call the macro M for each LSM hook MAX_LSM_COUNT times.
|
||||
*/
|
||||
#define LSM_LOOP_UNROLL(M, ...) \
|
||||
do { \
|
||||
UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) \
|
||||
} while (0)
|
||||
|
||||
#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)
|
||||
#include "lsm.h"
|
||||
|
||||
/*
|
||||
* These are descriptions of the reasons that can be passed to the
|
||||
@ -91,23 +74,35 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
|
||||
[LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
|
||||
};
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
|
||||
bool lsm_debug __ro_after_init;
|
||||
|
||||
static struct kmem_cache *lsm_file_cache;
|
||||
static struct kmem_cache *lsm_inode_cache;
|
||||
unsigned int lsm_active_cnt __ro_after_init;
|
||||
const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];
|
||||
|
||||
char *lsm_names;
|
||||
static struct lsm_blob_sizes blob_sizes __ro_after_init;
|
||||
struct lsm_blob_sizes blob_sizes;
|
||||
|
||||
/* Boot-time LSM user choice */
|
||||
static __initdata const char *chosen_lsm_order;
|
||||
static __initdata const char *chosen_major_lsm;
|
||||
struct kmem_cache *lsm_file_cache;
|
||||
struct kmem_cache *lsm_backing_file_cache;
|
||||
struct kmem_cache *lsm_inode_cache;
|
||||
|
||||
static __initconst const char *const builtin_lsm_order = CONFIG_LSM;
|
||||
#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX
|
||||
|
||||
/* Ordered list of LSMs to initialize. */
|
||||
static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1];
|
||||
static __initdata struct lsm_info *exclusive;
|
||||
/*
|
||||
* Identifier for the LSM static calls.
|
||||
* HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
|
||||
* IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
|
||||
*/
|
||||
#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX
|
||||
|
||||
/*
|
||||
* Call the macro M for each LSM hook MAX_LSM_COUNT times.
|
||||
*/
|
||||
#define LSM_LOOP_UNROLL(M, ...) \
|
||||
do { \
|
||||
UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) \
|
||||
} while (0)
|
||||
|
||||
#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)
|
||||
|
||||
#ifdef CONFIG_HAVE_STATIC_CALL
|
||||
#define LSM_HOOK_TRAMP(NAME, NUM) \
|
||||
@ -158,518 +153,51 @@ struct lsm_static_calls_table
|
||||
#undef INIT_LSM_STATIC_CALL
|
||||
};
|
||||
|
||||
static __initdata bool debug;
|
||||
#define init_debug(...) \
|
||||
do { \
|
||||
if (debug) \
|
||||
pr_info(__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static bool __init is_enabled(struct lsm_info *lsm)
|
||||
{
|
||||
if (!lsm->enabled)
|
||||
return false;
|
||||
|
||||
return *lsm->enabled;
|
||||
}
|
||||
|
||||
/* Mark an LSM's enabled flag. */
|
||||
static int lsm_enabled_true __initdata = 1;
|
||||
static int lsm_enabled_false __initdata = 0;
|
||||
static void __init set_enabled(struct lsm_info *lsm, bool enabled)
|
||||
{
|
||||
/*
|
||||
* When an LSM hasn't configured an enable variable, we can use
|
||||
* a hard-coded location for storing the default enabled state.
|
||||
*/
|
||||
if (!lsm->enabled) {
|
||||
if (enabled)
|
||||
lsm->enabled = &lsm_enabled_true;
|
||||
else
|
||||
lsm->enabled = &lsm_enabled_false;
|
||||
} else if (lsm->enabled == &lsm_enabled_true) {
|
||||
if (!enabled)
|
||||
lsm->enabled = &lsm_enabled_false;
|
||||
} else if (lsm->enabled == &lsm_enabled_false) {
|
||||
if (enabled)
|
||||
lsm->enabled = &lsm_enabled_true;
|
||||
} else {
|
||||
*lsm->enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
/* Is an LSM already listed in the ordered LSMs list? */
|
||||
static bool __init exists_ordered_lsm(struct lsm_info *lsm)
|
||||
{
|
||||
struct lsm_info **check;
|
||||
|
||||
for (check = ordered_lsms; *check; check++)
|
||||
if (*check == lsm)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Append an LSM to the list of ordered LSMs to initialize. */
|
||||
static int last_lsm __initdata;
|
||||
static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
|
||||
{
|
||||
/* Ignore duplicate selections. */
|
||||
if (exists_ordered_lsm(lsm))
|
||||
return;
|
||||
|
||||
if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from))
|
||||
return;
|
||||
|
||||
/* Enable this LSM, if it is not already set. */
|
||||
if (!lsm->enabled)
|
||||
lsm->enabled = &lsm_enabled_true;
|
||||
ordered_lsms[last_lsm++] = lsm;
|
||||
|
||||
init_debug("%s ordered: %s (%s)\n", from, lsm->name,
|
||||
is_enabled(lsm) ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
/* Is an LSM allowed to be initialized? */
|
||||
static bool __init lsm_allowed(struct lsm_info *lsm)
|
||||
{
|
||||
/* Skip if the LSM is disabled. */
|
||||
if (!is_enabled(lsm))
|
||||
return false;
|
||||
|
||||
/* Not allowed if another exclusive LSM already initialized. */
|
||||
if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
|
||||
init_debug("exclusive disabled: %s\n", lsm->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __init lsm_set_blob_size(int *need, int *lbs)
|
||||
{
|
||||
int offset;
|
||||
|
||||
if (*need <= 0)
|
||||
return;
|
||||
|
||||
offset = ALIGN(*lbs, sizeof(void *));
|
||||
*lbs = offset + *need;
|
||||
*need = offset;
|
||||
}
|
||||
|
||||
static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
|
||||
{
|
||||
if (!needed)
|
||||
return;
|
||||
|
||||
lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
|
||||
lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
|
||||
lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
|
||||
/*
|
||||
* The inode blob gets an rcu_head in addition to
|
||||
* what the modules might need.
|
||||
*/
|
||||
if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
|
||||
blob_sizes.lbs_inode = sizeof(struct rcu_head);
|
||||
lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
|
||||
lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
|
||||
lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key);
|
||||
lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
|
||||
lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
|
||||
lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
|
||||
lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
|
||||
lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
|
||||
lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
|
||||
lsm_set_blob_size(&needed->lbs_xattr_count,
|
||||
&blob_sizes.lbs_xattr_count);
|
||||
lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
|
||||
}
|
||||
|
||||
/* Prepare LSM for initialization. */
|
||||
static void __init prepare_lsm(struct lsm_info *lsm)
|
||||
{
|
||||
int enabled = lsm_allowed(lsm);
|
||||
|
||||
/* Record enablement (to handle any following exclusive LSMs). */
|
||||
set_enabled(lsm, enabled);
|
||||
|
||||
/* If enabled, do pre-initialization work. */
|
||||
if (enabled) {
|
||||
if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
|
||||
exclusive = lsm;
|
||||
init_debug("exclusive chosen: %s\n", lsm->name);
|
||||
}
|
||||
|
||||
lsm_set_blob_sizes(lsm->blobs);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize a given LSM, if it is enabled. */
|
||||
static void __init initialize_lsm(struct lsm_info *lsm)
|
||||
{
|
||||
if (is_enabled(lsm)) {
|
||||
int ret;
|
||||
|
||||
init_debug("initializing %s\n", lsm->name);
|
||||
ret = lsm->init();
|
||||
WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Current index to use while initializing the lsm id list.
|
||||
/**
|
||||
* lsm_file_alloc - allocate a composite file blob
|
||||
* @file: the file that needs a blob
|
||||
*
|
||||
* Allocate the file blob for all the modules
|
||||
*
|
||||
* Returns 0, or -ENOMEM if memory can't be allocated.
|
||||
*/
|
||||
u32 lsm_active_cnt __ro_after_init;
|
||||
const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];
|
||||
|
||||
/* Populate ordered LSMs list from comma-separated LSM name list. */
|
||||
static void __init ordered_lsm_parse(const char *order, const char *origin)
|
||||
static int lsm_file_alloc(struct file *file)
|
||||
{
|
||||
struct lsm_info *lsm;
|
||||
char *sep, *name, *next;
|
||||
|
||||
/* LSM_ORDER_FIRST is always first. */
|
||||
for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
|
||||
if (lsm->order == LSM_ORDER_FIRST)
|
||||
append_ordered_lsm(lsm, " first");
|
||||
}
|
||||
|
||||
/* Process "security=", if given. */
|
||||
if (chosen_major_lsm) {
|
||||
struct lsm_info *major;
|
||||
|
||||
/*
|
||||
* To match the original "security=" behavior, this
|
||||
* explicitly does NOT fallback to another Legacy Major
|
||||
* if the selected one was separately disabled: disable
|
||||
* all non-matching Legacy Major LSMs.
|
||||
*/
|
||||
for (major = __start_lsm_info; major < __end_lsm_info;
|
||||
major++) {
|
||||
if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
|
||||
strcmp(major->name, chosen_major_lsm) != 0) {
|
||||
set_enabled(major, false);
|
||||
init_debug("security=%s disabled: %s (only one legacy major LSM)\n",
|
||||
chosen_major_lsm, major->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sep = kstrdup(order, GFP_KERNEL);
|
||||
next = sep;
|
||||
/* Walk the list, looking for matching LSMs. */
|
||||
while ((name = strsep(&next, ",")) != NULL) {
|
||||
bool found = false;
|
||||
|
||||
for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
|
||||
if (strcmp(lsm->name, name) == 0) {
|
||||
if (lsm->order == LSM_ORDER_MUTABLE)
|
||||
append_ordered_lsm(lsm, origin);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
init_debug("%s ignored: %s (not built into kernel)\n",
|
||||
origin, name);
|
||||
}
|
||||
|
||||
/* Process "security=", if given. */
|
||||
if (chosen_major_lsm) {
|
||||
for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
|
||||
if (exists_ordered_lsm(lsm))
|
||||
continue;
|
||||
if (strcmp(lsm->name, chosen_major_lsm) == 0)
|
||||
append_ordered_lsm(lsm, "security=");
|
||||
}
|
||||
}
|
||||
|
||||
/* LSM_ORDER_LAST is always last. */
|
||||
for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
|
||||
if (lsm->order == LSM_ORDER_LAST)
|
||||
append_ordered_lsm(lsm, " last");
|
||||
}
|
||||
|
||||
/* Disable all LSMs not in the ordered list. */
|
||||
for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
|
||||
if (exists_ordered_lsm(lsm))
|
||||
continue;
|
||||
set_enabled(lsm, false);
|
||||
init_debug("%s skipped: %s (not in requested order)\n",
|
||||
origin, lsm->name);
|
||||
}
|
||||
|
||||
kfree(sep);
|
||||
}
|
||||
|
||||
static void __init lsm_static_call_init(struct security_hook_list *hl)
|
||||
{
|
||||
struct lsm_static_call *scall = hl->scalls;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_LSM_COUNT; i++) {
|
||||
/* Update the first static call that is not used yet */
|
||||
if (!scall->hl) {
|
||||
__static_call_update(scall->key, scall->trampoline,
|
||||
hl->hook.lsm_func_addr);
|
||||
scall->hl = hl;
|
||||
static_branch_enable(scall->active);
|
||||
return;
|
||||
}
|
||||
scall++;
|
||||
}
|
||||
panic("%s - Ran out of static slots.\n", __func__);
|
||||
}
|
||||
|
||||
static void __init lsm_early_cred(struct cred *cred);
|
||||
static void __init lsm_early_task(struct task_struct *task);
|
||||
|
||||
static int lsm_append(const char *new, char **result);
|
||||
|
||||
static void __init report_lsm_order(void)
|
||||
{
|
||||
struct lsm_info **lsm, *early;
|
||||
int first = 0;
|
||||
|
||||
pr_info("initializing lsm=");
|
||||
|
||||
/* Report each enabled LSM name, comma separated. */
|
||||
for (early = __start_early_lsm_info;
|
||||
early < __end_early_lsm_info; early++)
|
||||
if (is_enabled(early))
|
||||
pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
|
||||
for (lsm = ordered_lsms; *lsm; lsm++)
|
||||
if (is_enabled(*lsm))
|
||||
pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);
|
||||
|
||||
pr_cont("\n");
|
||||
}
|
||||
|
||||
static void __init ordered_lsm_init(void)
|
||||
{
|
||||
struct lsm_info **lsm;
|
||||
|
||||
if (chosen_lsm_order) {
|
||||
if (chosen_major_lsm) {
|
||||
pr_warn("security=%s is ignored because it is superseded by lsm=%s\n",
|
||||
chosen_major_lsm, chosen_lsm_order);
|
||||
chosen_major_lsm = NULL;
|
||||
}
|
||||
ordered_lsm_parse(chosen_lsm_order, "cmdline");
|
||||
} else
|
||||
ordered_lsm_parse(builtin_lsm_order, "builtin");
|
||||
|
||||
for (lsm = ordered_lsms; *lsm; lsm++)
|
||||
prepare_lsm(*lsm);
|
||||
|
||||
report_lsm_order();
|
||||
|
||||
init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
|
||||
init_debug("file blob size = %d\n", blob_sizes.lbs_file);
|
||||
init_debug("ib blob size = %d\n", blob_sizes.lbs_ib);
|
||||
init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
|
||||
init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
|
||||
#ifdef CONFIG_KEYS
|
||||
init_debug("key blob size = %d\n", blob_sizes.lbs_key);
|
||||
#endif /* CONFIG_KEYS */
|
||||
init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
|
||||
init_debug("sock blob size = %d\n", blob_sizes.lbs_sock);
|
||||
init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
|
||||
init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event);
|
||||
init_debug("task blob size = %d\n", blob_sizes.lbs_task);
|
||||
init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev);
|
||||
init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
|
||||
init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev);
|
||||
|
||||
/*
|
||||
* Create any kmem_caches needed for blobs
|
||||
*/
|
||||
if (blob_sizes.lbs_file)
|
||||
lsm_file_cache = kmem_cache_create("lsm_file_cache",
|
||||
blob_sizes.lbs_file, 0,
|
||||
SLAB_PANIC, NULL);
|
||||
if (blob_sizes.lbs_inode)
|
||||
lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
|
||||
blob_sizes.lbs_inode, 0,
|
||||
SLAB_PANIC, NULL);
|
||||
|
||||
lsm_early_cred((struct cred *) current->cred);
|
||||
lsm_early_task(current);
|
||||
for (lsm = ordered_lsms; *lsm; lsm++)
|
||||
initialize_lsm(*lsm);
|
||||
}
|
||||
|
||||
int __init early_security_init(void)
|
||||
{
|
||||
struct lsm_info *lsm;
|
||||
static bool early_security_initialized;
|
||||
|
||||
if (early_security_initialized)
|
||||
if (!lsm_file_cache) {
|
||||
file->f_security = NULL;
|
||||
return 0;
|
||||
|
||||
for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
|
||||
if (!lsm->enabled)
|
||||
lsm->enabled = &lsm_enabled_true;
|
||||
prepare_lsm(lsm);
|
||||
initialize_lsm(lsm);
|
||||
}
|
||||
|
||||
early_security_initialized = true;
|
||||
file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
|
||||
if (file->f_security == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* security_init - initializes the security framework
|
||||
* lsm_backing_file_alloc - allocate a composite backing file blob
|
||||
* @backing_file: the backing file
|
||||
*
|
||||
* This should be called early in the kernel initialization sequence.
|
||||
* Allocate the backing file blob for all the modules.
|
||||
*
|
||||
* Returns 0, or -ENOMEM if memory can't be allocated.
|
||||
*/
|
||||
int __init security_init(void)
|
||||
static int lsm_backing_file_alloc(struct file *backing_file)
|
||||
{
|
||||
struct lsm_info *lsm;
|
||||
void *blob;
|
||||
|
||||
init_debug("legacy security=%s\n", chosen_major_lsm ? : " *unspecified*");
|
||||
init_debug(" CONFIG_LSM=%s\n", builtin_lsm_order);
|
||||
init_debug("boot arg lsm=%s\n", chosen_lsm_order ? : " *unspecified*");
|
||||
|
||||
/*
|
||||
* Append the names of the early LSM modules now that kmalloc() is
|
||||
* available
|
||||
*/
|
||||
for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
|
||||
init_debug(" early started: %s (%s)\n", lsm->name,
|
||||
is_enabled(lsm) ? "enabled" : "disabled");
|
||||
if (lsm->enabled)
|
||||
lsm_append(lsm->name, &lsm_names);
|
||||
if (!lsm_backing_file_cache) {
|
||||
backing_file_set_security(backing_file, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Load LSMs in specified order. */
|
||||
ordered_lsm_init();
|
||||
|
||||
blob = kmem_cache_zalloc(lsm_backing_file_cache, GFP_KERNEL);
|
||||
backing_file_set_security(backing_file, blob);
|
||||
if (!blob)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save user chosen LSM */
|
||||
static int __init choose_major_lsm(char *str)
|
||||
{
|
||||
chosen_major_lsm = str;
|
||||
return 1;
|
||||
}
|
||||
__setup("security=", choose_major_lsm);
|
||||
|
||||
/* Explicitly choose LSM initialization order. */
|
||||
static int __init choose_lsm_order(char *str)
|
||||
{
|
||||
chosen_lsm_order = str;
|
||||
return 1;
|
||||
}
|
||||
__setup("lsm=", choose_lsm_order);
|
||||
|
||||
/* Enable LSM order debugging. */
|
||||
static int __init enable_debug(char *str)
|
||||
{
|
||||
debug = true;
|
||||
return 1;
|
||||
}
|
||||
__setup("lsm.debug", enable_debug);
|
||||
|
||||
static bool match_last_lsm(const char *list, const char *lsm)
|
||||
{
|
||||
const char *last;
|
||||
|
||||
if (WARN_ON(!list || !lsm))
|
||||
return false;
|
||||
last = strrchr(list, ',');
|
||||
if (last)
|
||||
/* Pass the comma, strcmp() will check for '\0' */
|
||||
last++;
|
||||
else
|
||||
last = list;
|
||||
return !strcmp(last, lsm);
|
||||
}
|
||||
|
||||
static int lsm_append(const char *new, char **result)
|
||||
{
|
||||
char *cp;
|
||||
|
||||
if (*result == NULL) {
|
||||
*result = kstrdup(new, GFP_KERNEL);
|
||||
if (*result == NULL)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
/* Check if it is the last registered name */
|
||||
if (match_last_lsm(*result, new))
|
||||
return 0;
|
||||
cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
|
||||
if (cp == NULL)
|
||||
return -ENOMEM;
|
||||
kfree(*result);
|
||||
*result = cp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* security_add_hooks - Add a modules hooks to the hook lists.
|
||||
* @hooks: the hooks to add
|
||||
* @count: the number of hooks to add
|
||||
* @lsmid: the identification information for the security module
|
||||
*
|
||||
* Each LSM has to register its hooks with the infrastructure.
|
||||
*/
|
||||
void __init security_add_hooks(struct security_hook_list *hooks, int count,
|
||||
const struct lsm_id *lsmid)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* A security module may call security_add_hooks() more
|
||||
* than once during initialization, and LSM initialization
|
||||
* is serialized. Landlock is one such case.
|
||||
* Look at the previous entry, if there is one, for duplication.
|
||||
*/
|
||||
if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) {
|
||||
if (lsm_active_cnt >= MAX_LSM_COUNT)
|
||||
panic("%s Too many LSMs registered.\n", __func__);
|
||||
lsm_idlist[lsm_active_cnt++] = lsmid;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
hooks[i].lsmid = lsmid;
|
||||
lsm_static_call_init(&hooks[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't try to append during early_security_init(), we'll come back
|
||||
* and fix this up afterwards.
|
||||
*/
|
||||
if (slab_is_available()) {
|
||||
if (lsm_append(lsmid->name, &lsm_names) < 0)
|
||||
panic("%s - Cannot get early memory.\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
int call_blocking_lsm_notifier(enum lsm_event event, void *data)
|
||||
{
|
||||
return blocking_notifier_call_chain(&blocking_lsm_notifier_chain,
|
||||
event, data);
|
||||
}
|
||||
EXPORT_SYMBOL(call_blocking_lsm_notifier);
|
||||
|
||||
int register_blocking_lsm_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(&blocking_lsm_notifier_chain,
|
||||
nb);
|
||||
}
|
||||
EXPORT_SYMBOL(register_blocking_lsm_notifier);
|
||||
|
||||
int unregister_blocking_lsm_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain,
|
||||
nb);
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
|
||||
|
||||
/**
|
||||
* lsm_blob_alloc - allocate a composite blob
|
||||
* @dest: the destination for the blob
|
||||
@ -702,46 +230,11 @@ static int lsm_blob_alloc(void **dest, size_t size, gfp_t gfp)
|
||||
*
|
||||
* Returns 0, or -ENOMEM if memory can't be allocated.
|
||||
*/
|
||||
static int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
|
||||
int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
|
||||
{
|
||||
return lsm_blob_alloc(&cred->security, blob_sizes.lbs_cred, gfp);
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_early_cred - during initialization allocate a composite cred blob
|
||||
* @cred: the cred that needs a blob
|
||||
*
|
||||
* Allocate the cred blob for all the modules
|
||||
*/
|
||||
static void __init lsm_early_cred(struct cred *cred)
|
||||
{
|
||||
int rc = lsm_cred_alloc(cred, GFP_KERNEL);
|
||||
|
||||
if (rc)
|
||||
panic("%s: Early cred alloc failed.\n", __func__);
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_file_alloc - allocate a composite file blob
|
||||
* @file: the file that needs a blob
|
||||
*
|
||||
* Allocate the file blob for all the modules
|
||||
*
|
||||
* Returns 0, or -ENOMEM if memory can't be allocated.
|
||||
*/
|
||||
static int lsm_file_alloc(struct file *file)
|
||||
{
|
||||
if (!lsm_file_cache) {
|
||||
file->f_security = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
|
||||
if (file->f_security == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_inode_alloc - allocate a composite inode blob
|
||||
* @inode: the inode that needs a blob
|
||||
@ -772,7 +265,7 @@ static int lsm_inode_alloc(struct inode *inode, gfp_t gfp)
|
||||
*
|
||||
* Returns 0, or -ENOMEM if memory can't be allocated.
|
||||
*/
|
||||
static int lsm_task_alloc(struct task_struct *task)
|
||||
int lsm_task_alloc(struct task_struct *task)
|
||||
{
|
||||
return lsm_blob_alloc(&task->security, blob_sizes.lbs_task, GFP_KERNEL);
|
||||
}
|
||||
@ -841,20 +334,6 @@ static int lsm_bdev_alloc(struct block_device *bdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_early_task - during initialization allocate a composite task blob
|
||||
* @task: the task that needs a blob
|
||||
*
|
||||
* Allocate the task blob for all the modules
|
||||
*/
|
||||
static void __init lsm_early_task(struct task_struct *task)
|
||||
{
|
||||
int rc = lsm_task_alloc(task);
|
||||
|
||||
if (rc)
|
||||
panic("%s: Early task alloc failed.\n", __func__);
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_superblock_alloc - allocate a composite superblock blob
|
||||
* @sb: the superblock that needs a blob
|
||||
@ -2897,6 +2376,57 @@ void security_file_free(struct file *file)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* security_backing_file_alloc() - Allocate and setup a backing file blob
|
||||
* @backing_file: the backing file
|
||||
* @user_file: the associated user visible file
|
||||
*
|
||||
* Allocate a backing file LSM blob and perform any necessary initialization of
|
||||
* the LSM blob. There will be some operations where the LSM will not have
|
||||
* access to @user_file after this point, so any important state associated
|
||||
* with @user_file that is important to the LSM should be captured in the
|
||||
* backing file's LSM blob.
|
||||
*
|
||||
* LSM's should avoid taking a reference to @user_file in this hook as it will
|
||||
* result in problems later when the system attempts to drop/put the file
|
||||
* references due to a circular dependency.
|
||||
*
|
||||
* Return: Return 0 if the hook is successful, negative values otherwise.
|
||||
*/
|
||||
int security_backing_file_alloc(struct file *backing_file,
|
||||
const struct file *user_file)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = lsm_backing_file_alloc(backing_file);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = call_int_hook(backing_file_alloc, backing_file, user_file);
|
||||
if (unlikely(rc))
|
||||
security_backing_file_free(backing_file);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* security_backing_file_free() - Free a backing file blob
|
||||
* @backing_file: the backing file
|
||||
*
|
||||
* Free any LSM state associate with a backing file's LSM blob, including the
|
||||
* blob itself.
|
||||
*/
|
||||
void security_backing_file_free(struct file *backing_file)
|
||||
{
|
||||
void *blob = backing_file_security(backing_file);
|
||||
|
||||
call_void_hook(backing_file_free, backing_file);
|
||||
|
||||
if (blob) {
|
||||
backing_file_set_security(backing_file, NULL);
|
||||
kmem_cache_free(lsm_backing_file_cache, blob);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* security_file_ioctl() - Check if an ioctl is allowed
|
||||
* @file: associated file
|
||||
@ -2985,6 +2515,32 @@ int security_mmap_file(struct file *file, unsigned long prot,
|
||||
flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* security_mmap_backing_file - Check if mmap'ing a backing file is allowed
|
||||
* @vma: the vm_area_struct for the mmap'd region
|
||||
* @backing_file: the backing file being mmap'd
|
||||
* @user_file: the user file being mmap'd
|
||||
*
|
||||
* Check permissions for a mmap operation on a stacked filesystem. This hook
|
||||
* is called after the security_mmap_file() and is responsible for authorizing
|
||||
* the mmap on @backing_file. It is important to note that the mmap operation
|
||||
* on @user_file has already been authorized and the @vma->vm_file has been
|
||||
* set to @backing_file.
|
||||
*
|
||||
* Return: Returns 0 if permission is granted.
|
||||
*/
|
||||
int security_mmap_backing_file(struct vm_area_struct *vma,
|
||||
struct file *backing_file,
|
||||
struct file *user_file)
|
||||
{
|
||||
/* recommended by the stackable filesystem devs */
|
||||
if (WARN_ON_ONCE(!(backing_file->f_mode & FMODE_BACKING)))
|
||||
return -EIO;
|
||||
|
||||
return call_int_hook(mmap_backing_file, vma, backing_file, user_file);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(security_mmap_backing_file);
|
||||
|
||||
/**
|
||||
* security_mmap_addr() - Check if mmap'ing an address is allowed
|
||||
* @addr: address
|
||||
|
||||
@ -1724,6 +1724,60 @@ static inline int file_path_has_perm(const struct cred *cred,
|
||||
static int bpf_fd_pass(const struct file *file, u32 sid);
|
||||
#endif
|
||||
|
||||
static int __file_has_perm(const struct cred *cred, const struct file *file,
|
||||
u32 av, bool bf_user_file)
|
||||
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
struct inode *inode;
|
||||
u32 ssid = cred_sid(cred);
|
||||
u32 tsid_fd;
|
||||
int rc;
|
||||
|
||||
if (bf_user_file) {
|
||||
struct backing_file_security_struct *bfsec;
|
||||
const struct path *path;
|
||||
|
||||
if (WARN_ON(!(file->f_mode & FMODE_BACKING)))
|
||||
return -EIO;
|
||||
|
||||
bfsec = selinux_backing_file(file);
|
||||
path = backing_file_user_path(file);
|
||||
tsid_fd = bfsec->uf_sid;
|
||||
inode = d_inode(path->dentry);
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_PATH;
|
||||
ad.u.path = *path;
|
||||
} else {
|
||||
struct file_security_struct *fsec = selinux_file(file);
|
||||
|
||||
tsid_fd = fsec->sid;
|
||||
inode = file_inode(file);
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_FILE;
|
||||
ad.u.file = file;
|
||||
}
|
||||
|
||||
if (ssid != tsid_fd) {
|
||||
rc = avc_has_perm(ssid, tsid_fd, SECCLASS_FD, FD__USE, &ad);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BPF_SYSCALL
|
||||
/* regardless of backing vs user file, use the underlying file here */
|
||||
rc = bpf_fd_pass(file, ssid);
|
||||
if (rc)
|
||||
return rc;
|
||||
#endif
|
||||
|
||||
/* av is zero if only checking access to the descriptor. */
|
||||
if (av)
|
||||
return inode_has_perm(cred, inode, av, &ad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check whether a task can use an open file descriptor to
|
||||
access an inode in a given way. Check access to the
|
||||
descriptor itself, and then use dentry_has_perm to
|
||||
@ -1732,41 +1786,10 @@ static int bpf_fd_pass(const struct file *file, u32 sid);
|
||||
has the same SID as the process. If av is zero, then
|
||||
access to the file is not checked, e.g. for cases
|
||||
where only the descriptor is affected like seek. */
|
||||
static int file_has_perm(const struct cred *cred,
|
||||
struct file *file,
|
||||
u32 av)
|
||||
static inline int file_has_perm(const struct cred *cred,
|
||||
const struct file *file, u32 av)
|
||||
{
|
||||
struct file_security_struct *fsec = selinux_file(file);
|
||||
struct inode *inode = file_inode(file);
|
||||
struct common_audit_data ad;
|
||||
u32 sid = cred_sid(cred);
|
||||
int rc;
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_FILE;
|
||||
ad.u.file = file;
|
||||
|
||||
if (sid != fsec->sid) {
|
||||
rc = avc_has_perm(sid, fsec->sid,
|
||||
SECCLASS_FD,
|
||||
FD__USE,
|
||||
&ad);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BPF_SYSCALL
|
||||
rc = bpf_fd_pass(file, cred_sid(cred));
|
||||
if (rc)
|
||||
return rc;
|
||||
#endif
|
||||
|
||||
/* av is zero if only checking access to the descriptor. */
|
||||
rc = 0;
|
||||
if (av)
|
||||
rc = inode_has_perm(cred, inode, av, &ad);
|
||||
|
||||
out:
|
||||
return rc;
|
||||
return __file_has_perm(cred, file, av, false);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3655,6 +3678,17 @@ static int selinux_file_alloc_security(struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int selinux_backing_file_alloc(struct file *backing_file,
|
||||
const struct file *user_file)
|
||||
{
|
||||
struct backing_file_security_struct *bfsec;
|
||||
|
||||
bfsec = selinux_backing_file(backing_file);
|
||||
bfsec->uf_sid = selinux_file(user_file)->sid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a task has the ioctl permission and cmd
|
||||
* operation to an inode.
|
||||
@ -3772,42 +3806,55 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd,
|
||||
|
||||
static int default_noexec __ro_after_init;
|
||||
|
||||
static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
|
||||
static int __file_map_prot_check(const struct cred *cred,
|
||||
const struct file *file, unsigned long prot,
|
||||
bool shared, bool bf_user_file)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
u32 sid = cred_sid(cred);
|
||||
int rc = 0;
|
||||
struct inode *inode = NULL;
|
||||
bool prot_exec = prot & PROT_EXEC;
|
||||
bool prot_write = prot & PROT_WRITE;
|
||||
|
||||
if (file) {
|
||||
if (bf_user_file)
|
||||
inode = d_inode(backing_file_user_path(file)->dentry);
|
||||
else
|
||||
inode = file_inode(file);
|
||||
}
|
||||
|
||||
if (default_noexec && prot_exec &&
|
||||
(!file || IS_PRIVATE(inode) || (!shared && prot_write))) {
|
||||
int rc;
|
||||
u32 sid = cred_sid(cred);
|
||||
|
||||
if (default_noexec &&
|
||||
(prot & PROT_EXEC) && (!file || IS_PRIVATE(file_inode(file)) ||
|
||||
(!shared && (prot & PROT_WRITE)))) {
|
||||
/*
|
||||
* We are making executable an anonymous mapping or a
|
||||
* private file mapping that will also be writable.
|
||||
* This has an additional check.
|
||||
* We are making executable an anonymous mapping or a private
|
||||
* file mapping that will also be writable.
|
||||
*/
|
||||
rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
|
||||
PROCESS__EXECMEM, NULL);
|
||||
rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__EXECMEM,
|
||||
NULL);
|
||||
if (rc)
|
||||
goto error;
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (file) {
|
||||
/* read access is always possible with a mapping */
|
||||
/* "read" always possible, "write" only if shared */
|
||||
u32 av = FILE__READ;
|
||||
|
||||
/* write access only matters if the mapping is shared */
|
||||
if (shared && (prot & PROT_WRITE))
|
||||
if (shared && prot_write)
|
||||
av |= FILE__WRITE;
|
||||
|
||||
if (prot & PROT_EXEC)
|
||||
if (prot_exec)
|
||||
av |= FILE__EXECUTE;
|
||||
|
||||
return file_has_perm(cred, file, av);
|
||||
return __file_has_perm(cred, file, av, bf_user_file);
|
||||
}
|
||||
|
||||
error:
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int file_map_prot_check(const struct cred *cred,
|
||||
const struct file *file,
|
||||
unsigned long prot, bool shared)
|
||||
{
|
||||
return __file_map_prot_check(cred, file, prot, shared, false);
|
||||
}
|
||||
|
||||
static int selinux_mmap_addr(unsigned long addr)
|
||||
@ -3823,36 +3870,80 @@ static int selinux_mmap_addr(unsigned long addr)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int selinux_mmap_file(struct file *file,
|
||||
unsigned long reqprot __always_unused,
|
||||
unsigned long prot, unsigned long flags)
|
||||
static int selinux_mmap_file_common(const struct cred *cred, struct file *file,
|
||||
unsigned long prot, bool shared)
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
int rc;
|
||||
|
||||
if (file) {
|
||||
int rc;
|
||||
struct common_audit_data ad;
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_FILE;
|
||||
ad.u.file = file;
|
||||
rc = inode_has_perm(current_cred(), file_inode(file),
|
||||
FILE__MAP, &ad);
|
||||
rc = inode_has_perm(cred, file_inode(file), FILE__MAP, &ad);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return file_map_prot_check(file, prot,
|
||||
(flags & MAP_TYPE) == MAP_SHARED);
|
||||
return file_map_prot_check(cred, file, prot, shared);
|
||||
}
|
||||
|
||||
static int selinux_mmap_file(struct file *file,
|
||||
unsigned long reqprot __always_unused,
|
||||
unsigned long prot, unsigned long flags)
|
||||
{
|
||||
return selinux_mmap_file_common(current_cred(), file, prot,
|
||||
(flags & MAP_TYPE) == MAP_SHARED);
|
||||
}
|
||||
|
||||
/**
|
||||
* selinux_mmap_backing_file - Check mmap permissions on a backing file
|
||||
* @vma: memory region
|
||||
* @backing_file: stacked filesystem backing file
|
||||
* @user_file: user visible file
|
||||
*
|
||||
* This is called after selinux_mmap_file() on stacked filesystems, and it
|
||||
* is this function's responsibility to verify access to @backing_file and
|
||||
* setup the SELinux state for possible later use in the mprotect() code path.
|
||||
*
|
||||
* By the time this function is called, mmap() access to @user_file has already
|
||||
* been authorized and @vma->vm_file has been set to point to @backing_file.
|
||||
*
|
||||
* Return zero on success, negative values otherwise.
|
||||
*/
|
||||
static int selinux_mmap_backing_file(struct vm_area_struct *vma,
|
||||
struct file *backing_file,
|
||||
struct file *user_file __always_unused)
|
||||
{
|
||||
unsigned long prot = 0;
|
||||
|
||||
/* translate vma->vm_flags perms into PROT perms */
|
||||
if (vma->vm_flags & VM_READ)
|
||||
prot |= PROT_READ;
|
||||
if (vma->vm_flags & VM_WRITE)
|
||||
prot |= PROT_WRITE;
|
||||
if (vma->vm_flags & VM_EXEC)
|
||||
prot |= PROT_EXEC;
|
||||
|
||||
return selinux_mmap_file_common(backing_file->f_cred, backing_file,
|
||||
prot, vma->vm_flags & VM_SHARED);
|
||||
}
|
||||
|
||||
static int selinux_file_mprotect(struct vm_area_struct *vma,
|
||||
unsigned long reqprot __always_unused,
|
||||
unsigned long prot)
|
||||
{
|
||||
int rc;
|
||||
const struct cred *cred = current_cred();
|
||||
u32 sid = cred_sid(cred);
|
||||
const struct file *file = vma->vm_file;
|
||||
bool backing_file;
|
||||
bool shared = vma->vm_flags & VM_SHARED;
|
||||
|
||||
/* check if we need to trigger the "backing files are awful" mode */
|
||||
backing_file = file && (file->f_mode & FMODE_BACKING);
|
||||
|
||||
if (default_noexec &&
|
||||
(prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
|
||||
int rc = 0;
|
||||
/*
|
||||
* We don't use the vma_is_initial_heap() helper as it has
|
||||
* a history of problems and is currently broken on systems
|
||||
@ -3866,11 +3957,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
|
||||
vma->vm_end <= vma->vm_mm->brk) {
|
||||
rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
|
||||
PROCESS__EXECHEAP, NULL);
|
||||
} else if (!vma->vm_file && (vma_is_initial_stack(vma) ||
|
||||
if (rc)
|
||||
return rc;
|
||||
} else if (!file && (vma_is_initial_stack(vma) ||
|
||||
vma_is_stack_for_current(vma))) {
|
||||
rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
|
||||
PROCESS__EXECSTACK, NULL);
|
||||
} else if (vma->vm_file && vma->anon_vma) {
|
||||
if (rc)
|
||||
return rc;
|
||||
} else if (file && vma->anon_vma) {
|
||||
/*
|
||||
* We are making executable a file mapping that has
|
||||
* had some COW done. Since pages might have been
|
||||
@ -3878,13 +3973,29 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
|
||||
* modified content. This typically should only
|
||||
* occur for text relocations.
|
||||
*/
|
||||
rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD);
|
||||
rc = __file_has_perm(cred, file, FILE__EXECMOD,
|
||||
backing_file);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (backing_file) {
|
||||
rc = file_has_perm(file->f_cred, file,
|
||||
FILE__EXECMOD);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc = __file_map_prot_check(cred, file, prot, shared, backing_file);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (backing_file) {
|
||||
rc = file_map_prot_check(file->f_cred, file, prot, shared);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int selinux_file_lock(struct file *file, unsigned int cmd)
|
||||
@ -6963,6 +7074,7 @@ static void selinux_bpf_token_free(struct bpf_token *token)
|
||||
struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
|
||||
.lbs_cred = sizeof(struct task_security_struct),
|
||||
.lbs_file = sizeof(struct file_security_struct),
|
||||
.lbs_backing_file = sizeof(struct backing_file_security_struct),
|
||||
.lbs_inode = sizeof(struct inode_security_struct),
|
||||
.lbs_ipc = sizeof(struct ipc_security_struct),
|
||||
.lbs_key = sizeof(struct key_security_struct),
|
||||
@ -7168,9 +7280,11 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
|
||||
|
||||
LSM_HOOK_INIT(file_permission, selinux_file_permission),
|
||||
LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
|
||||
LSM_HOOK_INIT(backing_file_alloc, selinux_backing_file_alloc),
|
||||
LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
|
||||
LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat),
|
||||
LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
|
||||
LSM_HOOK_INIT(mmap_backing_file, selinux_mmap_backing_file),
|
||||
LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr),
|
||||
LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect),
|
||||
LSM_HOOK_INIT(file_lock, selinux_file_lock),
|
||||
@ -7440,7 +7554,7 @@ void selinux_complete_init(void)
|
||||
/* SELinux requires early initialization in order to label
|
||||
all processes and objects when they are created. */
|
||||
DEFINE_LSM(selinux) = {
|
||||
.name = "selinux",
|
||||
.id = &selinux_lsmid,
|
||||
.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
|
||||
.enabled = &selinux_enabled_boot,
|
||||
.blobs = &selinux_blob_sizes,
|
||||
|
||||
@ -61,6 +61,10 @@ struct file_security_struct {
|
||||
u32 pseqno; /* Policy seqno at the time of file open */
|
||||
};
|
||||
|
||||
struct backing_file_security_struct {
|
||||
u32 uf_sid; /* associated user file fsec->sid */
|
||||
};
|
||||
|
||||
struct superblock_security_struct {
|
||||
u32 sid; /* SID of file system superblock */
|
||||
u32 def_sid; /* default SID for labeling */
|
||||
@ -159,6 +163,13 @@ static inline struct file_security_struct *selinux_file(const struct file *file)
|
||||
return file->f_security + selinux_blob_sizes.lbs_file;
|
||||
}
|
||||
|
||||
static inline struct backing_file_security_struct *
|
||||
selinux_backing_file(const struct file *backing_file)
|
||||
{
|
||||
void *blob = backing_file_security(backing_file);
|
||||
return blob + selinux_blob_sizes.lbs_backing_file;
|
||||
}
|
||||
|
||||
static inline struct inode_security_struct *
|
||||
selinux_inode(const struct inode *inode)
|
||||
{
|
||||
|
||||
@ -5278,7 +5278,7 @@ static __init int smack_init(void)
|
||||
* all processes and objects when they are created.
|
||||
*/
|
||||
DEFINE_LSM(smack) = {
|
||||
.name = "smack",
|
||||
.id = &smack_lsmid,
|
||||
.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
|
||||
.blobs = &smack_blob_sizes,
|
||||
.init = smack_init,
|
||||
|
||||
@ -615,7 +615,7 @@ static int __init tomoyo_init(void)
|
||||
}
|
||||
|
||||
DEFINE_LSM(tomoyo) = {
|
||||
.name = "tomoyo",
|
||||
.id = &tomoyo_lsmid,
|
||||
.enabled = &tomoyo_enabled,
|
||||
.flags = LSM_FLAG_LEGACY_MAJOR,
|
||||
.blobs = &tomoyo_blob_sizes,
|
||||
|
||||
@ -483,6 +483,6 @@ static int __init yama_init(void)
|
||||
}
|
||||
|
||||
DEFINE_LSM(yama) = {
|
||||
.name = "yama",
|
||||
.id = &yama_lsmid,
|
||||
.init = yama_init,
|
||||
};
|
||||
|
||||
@ -53,11 +53,6 @@ static void usb6fire_chip_abort(struct sfire_chip *chip)
|
||||
usb6fire_comm_abort(chip);
|
||||
if (chip->control)
|
||||
usb6fire_control_abort(chip);
|
||||
if (chip->card) {
|
||||
snd_card_disconnect(chip->card);
|
||||
snd_card_free_when_closed(chip->card);
|
||||
chip->card = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,6 +163,7 @@ destroy_chip:
|
||||
static void usb6fire_chip_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct sfire_chip *chip;
|
||||
struct snd_card *card;
|
||||
|
||||
chip = usb_get_intfdata(intf);
|
||||
if (chip) { /* if !chip, fw upload has been performed */
|
||||
@ -178,8 +174,19 @@ static void usb6fire_chip_disconnect(struct usb_interface *intf)
|
||||
chips[chip->regidx] = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save card pointer before teardown.
|
||||
* snd_card_free_when_closed() may free card (and
|
||||
* the embedded chip) immediately, so it must be
|
||||
* called last and chip must not be accessed after.
|
||||
*/
|
||||
card = chip->card;
|
||||
chip->shutdown = true;
|
||||
if (card)
|
||||
snd_card_disconnect(card);
|
||||
usb6fire_chip_abort(chip);
|
||||
if (card)
|
||||
snd_card_free_when_closed(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
|
||||
kernel-uki-virt-addons.almalinux,1,AlmaLinux,kernel-uki-virt-addons,6.12.0-211.20.1.el10.x86_64,mailto:security@almalinux.org
|
||||
kernel-uki-virt-addons.almalinux,1,AlmaLinux,kernel-uki-virt-addons,6.12.0-211.22.1.el10.x86_64,mailto:security@almalinux.org
|
||||
|
||||
2
uki.sbat
2
uki.sbat
@ -1,2 +1,2 @@
|
||||
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
|
||||
kernel-uki-virt.almalinux,1,AlmaLinux,kernel-uki-virt,6.12.0-211.20.1.el10.x86_64,mailto:security@almalinux.org
|
||||
kernel-uki-virt.almalinux,1,AlmaLinux,kernel-uki-virt,6.12.0-211.22.1.el10.x86_64,mailto:security@almalinux.org
|
||||
|
||||
Loading…
Reference in New Issue
Block a user