Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'exynos-drm-next-for-v6.20' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next

Fix three regressions
. Fix a regression where vidi_connection_ioctl() used the wrong device
to look up the vidi context. It stores the vidi device in exynos_drm_private
and uses it in ioctl(), preventing invalid pointer access and related bugs.
. Fix a security regression where vidi_connection_ioctl() directly dereferenced
a user pointer for EDID data. It copies EDID from user space
with copy_from_user() into kernel memory before use, preventing arbitrary
kernel memory access.
. Fix a concurrency regression where vidi_context members related
to EDID memory were accessed without locking. It protects alloc/free and
state updates with ctx->lock, preventing race conditions and use-after-free bugs.

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Inki Dae <inki.dae@samsung.com>
Link: https://patch.msgid.link/20260201143939.27074-1-inki.dae@samsung.com

+64 -11
+1
drivers/gpu/drm/exynos/exynos_drm_drv.h
··· 199 199 struct exynos_drm_private { 200 200 struct device *g2d_dev; 201 201 struct device *dma_dev; 202 + struct device *vidi_dev; 202 203 void *mapping; 203 204 204 205 /* for atomic commit */
+63 -11
drivers/gpu/drm/exynos/exynos_drm_vidi.c
··· 187 187 const char *buf, size_t len) 188 188 { 189 189 struct vidi_context *ctx = dev_get_drvdata(dev); 190 - int ret; 190 + int ret, new_connected; 191 191 192 - ret = kstrtoint(buf, 0, &ctx->connected); 192 + ret = kstrtoint(buf, 0, &new_connected); 193 193 if (ret) 194 194 return ret; 195 - 196 - if (ctx->connected > 1) 195 + if (new_connected > 1) 197 196 return -EINVAL; 197 + 198 + mutex_lock(&ctx->lock); 198 199 199 200 /* 200 201 * Use fake edid data for test. If raw_edid is set then it can't be ··· 203 202 */ 204 203 if (ctx->raw_edid) { 205 204 DRM_DEV_DEBUG_KMS(dev, "edid data is not fake data.\n"); 206 - return -EINVAL; 205 + ret = -EINVAL; 206 + goto fail; 207 207 } 208 + 209 + ctx->connected = new_connected; 210 + mutex_unlock(&ctx->lock); 208 211 209 212 DRM_DEV_DEBUG_KMS(dev, "requested connection.\n"); 210 213 211 214 drm_helper_hpd_irq_event(ctx->drm_dev); 212 215 213 216 return len; 217 + fail: 218 + mutex_unlock(&ctx->lock); 219 + return ret; 214 220 } 215 221 216 222 static DEVICE_ATTR(connection, 0644, vidi_show_connection, ··· 232 224 int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, 233 225 struct drm_file *file_priv) 234 226 { 235 - struct vidi_context *ctx = dev_get_drvdata(drm_dev->dev); 227 + struct exynos_drm_private *priv = drm_dev->dev_private; 228 + struct device *dev = priv ? priv->vidi_dev : NULL; 229 + struct vidi_context *ctx = dev ? dev_get_drvdata(dev) : NULL; 236 230 struct drm_exynos_vidi_connection *vidi = data; 231 + 232 + if (!ctx) 233 + return -ENODEV; 237 234 238 235 if (!vidi) { 239 236 DRM_DEV_DEBUG_KMS(ctx->dev, ··· 252 239 return -EINVAL; 253 240 } 254 241 242 + mutex_lock(&ctx->lock); 255 243 if (ctx->connected == vidi->connection) { 244 + mutex_unlock(&ctx->lock); 256 245 DRM_DEV_DEBUG_KMS(ctx->dev, 257 246 "same connection request.\n"); 258 247 return -EINVAL; 259 248 } 249 + mutex_unlock(&ctx->lock); 260 250 261 251 if (vidi->connection) { 262 252 const struct drm_edid *drm_edid; 263 - const struct edid *raw_edid; 253 + const void __user *edid_userptr = u64_to_user_ptr(vidi->edid); 254 + void *edid_buf; 255 + struct edid hdr; 264 256 size_t size; 265 257 266 - raw_edid = (const struct edid *)(unsigned long)vidi->edid; 267 - size = (raw_edid->extensions + 1) * EDID_LENGTH; 258 + if (copy_from_user(&hdr, edid_userptr, sizeof(hdr))) 259 + return -EFAULT; 268 260 269 - drm_edid = drm_edid_alloc(raw_edid, size); 261 + size = (hdr.extensions + 1) * EDID_LENGTH; 262 + 263 + edid_buf = kmalloc(size, GFP_KERNEL); 264 + if (!edid_buf) 265 + return -ENOMEM; 266 + 267 + if (copy_from_user(edid_buf, edid_userptr, size)) { 268 + kfree(edid_buf); 269 + return -EFAULT; 270 + } 271 + 272 + drm_edid = drm_edid_alloc(edid_buf, size); 273 + kfree(edid_buf); 270 274 if (!drm_edid) 271 275 return -ENOMEM; 272 276 ··· 293 263 "edid data is invalid.\n"); 294 264 return -EINVAL; 295 265 } 266 + mutex_lock(&ctx->lock); 296 267 ctx->raw_edid = drm_edid; 268 + mutex_unlock(&ctx->lock); 297 269 } else { 298 270 /* with connection = 0, free raw_edid */ 271 + mutex_lock(&ctx->lock); 299 272 drm_edid_free(ctx->raw_edid); 300 273 ctx->raw_edid = NULL; 274 + mutex_unlock(&ctx->lock); 301 275 } 302 276 277 + mutex_lock(&ctx->lock); 303 278 ctx->connected = vidi->connection; 279 + mutex_unlock(&ctx->lock); 280 + 304 281 drm_helper_hpd_irq_event(ctx->drm_dev); 305 282 306 283 return 0; ··· 322 285 * connection request would come from user side 323 286 * to do hotplug through specific ioctl. 324 287 */ 325 - return ctx->connected ? connector_status_connected : 288 + return READ_ONCE(ctx->connected) ? connector_status_connected : 326 289 connector_status_disconnected; 327 290 } 328 291 ··· 345 308 const struct drm_edid *drm_edid; 346 309 int count; 347 310 311 + mutex_lock(&ctx->lock); 312 + 348 313 if (ctx->raw_edid) 349 314 drm_edid = drm_edid_dup(ctx->raw_edid); 350 315 else 351 316 drm_edid = drm_edid_alloc(fake_edid_info, sizeof(fake_edid_info)); 317 + 318 + mutex_unlock(&ctx->lock); 352 319 353 320 drm_edid_connector_update(connector, drm_edid); 354 321 ··· 413 372 { 414 373 struct vidi_context *ctx = dev_get_drvdata(dev); 415 374 struct drm_device *drm_dev = data; 375 + struct exynos_drm_private *priv = drm_dev->dev_private; 416 376 struct drm_encoder *encoder = &ctx->encoder; 417 377 struct exynos_drm_plane *exynos_plane; 418 378 struct exynos_drm_plane_config plane_config = { 0 }; ··· 421 379 int ret; 422 380 423 381 ctx->drm_dev = drm_dev; 382 + if (priv) 383 + priv->vidi_dev = dev; 424 384 425 385 plane_config.pixel_formats = formats; 426 386 plane_config.num_pixel_formats = ARRAY_SIZE(formats); ··· 468 424 static void vidi_unbind(struct device *dev, struct device *master, void *data) 469 425 { 470 426 struct vidi_context *ctx = dev_get_drvdata(dev); 427 + struct drm_device *drm_dev = data; 428 + struct exynos_drm_private *priv = drm_dev->dev_private; 471 429 472 430 timer_delete_sync(&ctx->timer); 431 + if (priv) 432 + priv->vidi_dev = NULL; 473 433 } 474 434 475 435 static const struct component_ops vidi_component_ops = { ··· 505 457 { 506 458 struct vidi_context *ctx = platform_get_drvdata(pdev); 507 459 460 + mutex_lock(&ctx->lock); 461 + 508 462 drm_edid_free(ctx->raw_edid); 509 463 ctx->raw_edid = NULL; 464 + 465 + mutex_unlock(&ctx->lock); 510 466 511 467 component_del(&pdev->dev, &vidi_component_ops); 512 468 }